-
Notifications
You must be signed in to change notification settings - Fork 33
Add setUserWriteBlockMode tests
#658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ea141ee
e7cd17b
5755069
750b73e
95dd116
6f21130
8731df1
dd69334
1e420bb
34389ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| """Tests for setUserWriteBlockMode argument validation errors. | ||
|
|
||
| Validates type rejection for the global and reason fields, missing required fields, | ||
| invalid enum values, and unrecognized fields. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import pytest | ||
| from bson import Decimal128, Int64 | ||
|
|
||
| from documentdb_tests.compatibility.tests.core.utils.command_test_case import ( | ||
| CommandContext, | ||
| ) | ||
| from documentdb_tests.compatibility.tests.system.administration.commands.setUserWriteBlockMode.utils.write_block_helpers import ( # noqa: E501 | ||
| force_disable_write_block, | ||
| ) | ||
| from documentdb_tests.compatibility.tests.system.administration.commands.utils.admin_test_case import ( # noqa: E501 | ||
| AdminTestCase, | ||
| ) | ||
| from documentdb_tests.framework.assertions import assertFailureCode | ||
| from documentdb_tests.framework.error_codes import ( | ||
| BAD_VALUE_ERROR, | ||
| MISSING_FIELD_ERROR, | ||
| TYPE_MISMATCH_ERROR, | ||
| UNRECOGNIZED_COMMAND_FIELD_ERROR, | ||
| ) | ||
| from documentdb_tests.framework.executor import execute_admin_command | ||
| from documentdb_tests.framework.parametrize import pytest_params | ||
| from documentdb_tests.framework.test_constants import FLOAT_INFINITY, FLOAT_NAN | ||
|
|
||
| pytestmark = [pytest.mark.admin, pytest.mark.no_parallel, pytest.mark.requires(cluster_admin=True)] | ||
|
|
||
|
|
||
| @pytest.fixture(autouse=True) | ||
| def _manage_write_block(collection): | ||
| """Ensure write block is disabled before and after each test.""" | ||
| force_disable_write_block(collection) | ||
| yield | ||
| force_disable_write_block(collection) | ||
|
|
||
|
|
||
| # Property [Global Field Type Rejection]: setUserWriteBlockMode rejects all non-boolean types | ||
| # for the global field with no coercion. | ||
| GLOBAL_TYPE_REJECTION_TESTS: list[AdminTestCase] = [ | ||
| AdminTestCase( | ||
| f"global_type_{tid}", | ||
| command=lambda ctx, v=val: {"setUserWriteBlockMode": 1, "global": v}, | ||
| error_code=error, | ||
| msg=f"setUserWriteBlockMode should reject {tid} for global field", | ||
| ) | ||
| for tid, val, error in [ | ||
| ("int32_1", 1, TYPE_MISMATCH_ERROR), | ||
| ("int32_0", 0, TYPE_MISMATCH_ERROR), | ||
| ("double_1", 1.0, TYPE_MISMATCH_ERROR), | ||
| ("double_0", 0.0, TYPE_MISMATCH_ERROR), | ||
| ("int64", Int64(1), TYPE_MISMATCH_ERROR), | ||
| ("decimal128", Decimal128("1"), TYPE_MISMATCH_ERROR), | ||
| ("nan", FLOAT_NAN, TYPE_MISMATCH_ERROR), | ||
| ("infinity", FLOAT_INFINITY, TYPE_MISMATCH_ERROR), | ||
| ("negative_infinity", float("-inf"), TYPE_MISMATCH_ERROR), | ||
| ("negative_zero", -0.0, TYPE_MISMATCH_ERROR), | ||
| ("string", "true", TYPE_MISMATCH_ERROR), | ||
| ("array", [], TYPE_MISMATCH_ERROR), | ||
| ("object", {}, TYPE_MISMATCH_ERROR), | ||
| ] | ||
| ] | ||
|
|
||
| # Property [Missing Global Field]: setUserWriteBlockMode requires the global field. | ||
| # Null is treated as missing. | ||
| MISSING_GLOBAL_TESTS: list[AdminTestCase] = [ | ||
| AdminTestCase( | ||
| "missing_global", | ||
| command=lambda ctx: {"setUserWriteBlockMode": 1}, | ||
| error_code=MISSING_FIELD_ERROR, | ||
| msg="setUserWriteBlockMode should require the global field", | ||
| ), | ||
| AdminTestCase( | ||
| "global_null_treated_as_missing", | ||
| command=lambda ctx: {"setUserWriteBlockMode": 1, "global": None}, | ||
| error_code=MISSING_FIELD_ERROR, | ||
| msg="setUserWriteBlockMode should treat null global as missing", | ||
| ), | ||
| ] | ||
|
|
||
| # Property [Reason Field Type Rejection]: setUserWriteBlockMode rejects non-string types for | ||
| # the reason field. | ||
| REASON_TYPE_REJECTION_TESTS: list[AdminTestCase] = [ | ||
| AdminTestCase( | ||
| f"reason_type_{tid}", | ||
| command=lambda ctx, v=val: { | ||
| "setUserWriteBlockMode": 1, | ||
| "global": True, | ||
| "reason": v, | ||
| }, | ||
| error_code=TYPE_MISMATCH_ERROR, | ||
| msg=f"setUserWriteBlockMode should reject {tid} for reason field", | ||
| ) | ||
| for tid, val in [ | ||
| ("int", 1), | ||
| ("bool", True), | ||
| ("array", []), | ||
| ("object", {}), | ||
| ] | ||
| ] | ||
|
|
||
| # Property [Reason Field Invalid Enum]: setUserWriteBlockMode rejects unrecognized reason | ||
| # strings. | ||
| REASON_INVALID_ENUM_TESTS: list[AdminTestCase] = [ | ||
| AdminTestCase( | ||
| "reason_invalid_enum", | ||
| command=lambda ctx: { | ||
| "setUserWriteBlockMode": 1, | ||
| "global": True, | ||
| "reason": "InvalidReason", | ||
| }, | ||
| error_code=BAD_VALUE_ERROR, | ||
| msg="setUserWriteBlockMode should reject unrecognized reason enum value", | ||
| ), | ||
| ] | ||
|
|
||
| # Property [Unrecognized Fields]: setUserWriteBlockMode rejects unknown fields. | ||
| UNRECOGNIZED_FIELD_TESTS: list[AdminTestCase] = [ | ||
| AdminTestCase( | ||
| "unrecognized_field", | ||
| command=lambda ctx: { | ||
| "setUserWriteBlockMode": 1, | ||
| "global": False, | ||
| "unknownField": 1, | ||
| }, | ||
| error_code=UNRECOGNIZED_COMMAND_FIELD_ERROR, | ||
| msg="setUserWriteBlockMode should reject unrecognized fields", | ||
| ), | ||
| ] | ||
|
|
||
| ARGUMENT_ERROR_TESTS: list[AdminTestCase] = ( | ||
| GLOBAL_TYPE_REJECTION_TESTS | ||
| + MISSING_GLOBAL_TESTS | ||
| + REASON_TYPE_REJECTION_TESTS | ||
| + REASON_INVALID_ENUM_TESTS | ||
| + UNRECOGNIZED_FIELD_TESTS | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("test", pytest_params(ARGUMENT_ERROR_TESTS)) | ||
| def test_setUserWriteBlockMode_argument_error(collection, test): | ||
| """Test setUserWriteBlockMode rejects invalid arguments.""" | ||
| ctx = CommandContext.from_collection(collection) | ||
| result = execute_admin_command(collection, test.build_command(ctx)) | ||
| assertFailureCode(result, test.error_code, msg=test.msg) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| """Tests for setUserWriteBlockMode core behavior. | ||
|
|
||
| Validates enable/disable semantics, idempotent behavior, and state restoration. | ||
| """ | ||
|
|
||
| import pytest | ||
|
|
||
| from documentdb_tests.compatibility.tests.system.administration.commands.setUserWriteBlockMode.utils.write_block_helpers import ( # noqa: E501 | ||
| force_disable_write_block, | ||
| ) | ||
| from documentdb_tests.framework.assertions import assertSuccessPartial | ||
| from documentdb_tests.framework.executor import execute_admin_command, execute_command | ||
|
|
||
| pytestmark = [pytest.mark.admin, pytest.mark.no_parallel, pytest.mark.requires(cluster_admin=True)] | ||
|
|
||
|
|
||
| @pytest.fixture(autouse=True) | ||
| def _manage_write_block(collection): | ||
| """Ensure write block is disabled before and after each test.""" | ||
| force_disable_write_block(collection) | ||
| yield | ||
| force_disable_write_block(collection) | ||
|
|
||
|
|
||
| # Property [Idempotent Disable]: disabling write block when no block is active succeeds. | ||
| def test_setUserWriteBlockMode_disable_when_no_block_active(collection): | ||
| """Test setUserWriteBlockMode global:false when no block is active succeeds.""" | ||
| result = execute_admin_command(collection, {"setUserWriteBlockMode": 1, "global": False}) | ||
| assertSuccessPartial( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we additional test with actual write whether its blocking or not |
||
| result, | ||
| {"ok": 1.0}, | ||
| msg="setUserWriteBlockMode should succeed when disabling with no active block", | ||
| ) | ||
|
|
||
|
|
||
| # Property [Write Restoration]: writes succeed after disabling a previously active block. | ||
| def test_setUserWriteBlockMode_enable_disable_restores_writes(collection): | ||
| """Test setUserWriteBlockMode enable then disable allows writes again.""" | ||
| execute_admin_command(collection, {"setUserWriteBlockMode": 1, "global": True}) | ||
| execute_admin_command(collection, {"setUserWriteBlockMode": 1, "global": False}) | ||
| result = execute_command( | ||
| collection, {"insert": collection.name, "documents": [{"_id": "restore_test"}]} | ||
| ) | ||
| assertSuccessPartial( | ||
| result, | ||
| {"ok": 1.0}, | ||
| msg="setUserWriteBlockMode should allow writes after block is disabled", | ||
| ) | ||
|
|
||
|
|
||
| # Property [Repeated Toggle]: toggling write block multiple times does not produce errors. | ||
| def test_setUserWriteBlockMode_toggle_multiple_times(collection): | ||
| """Test setUserWriteBlockMode toggling on and off multiple times succeeds.""" | ||
| execute_admin_command(collection, {"setUserWriteBlockMode": 1, "global": True}) | ||
| execute_admin_command(collection, {"setUserWriteBlockMode": 1, "global": False}) | ||
| execute_admin_command(collection, {"setUserWriteBlockMode": 1, "global": True}) | ||
| execute_admin_command(collection, {"setUserWriteBlockMode": 1, "global": False}) | ||
| result = execute_admin_command(collection, {"setUserWriteBlockMode": 1, "global": True}) | ||
| assertSuccessPartial( | ||
| result, | ||
| {"ok": 1.0}, | ||
| msg="setUserWriteBlockMode should succeed after repeated toggling", | ||
| ) | ||
|
|
||
|
|
||
| # Property [Idempotent Enable]: re-enabling with same default reason is idempotent. | ||
| def test_setUserWriteBlockMode_enable_idempotent_same_reason(collection): | ||
| """Test setUserWriteBlockMode re-enable with same reason is idempotent.""" | ||
| execute_admin_command(collection, {"setUserWriteBlockMode": 1, "global": True}) | ||
| result = execute_admin_command(collection, {"setUserWriteBlockMode": 1, "global": True}) | ||
| assertSuccessPartial( | ||
| result, | ||
| {"ok": 1.0}, | ||
| msg="setUserWriteBlockMode should be idempotent when re-enabling with same reason", | ||
| ) | ||
|
|
||
|
|
||
| # Property [Same Explicit Reason Idempotent]: re-enabling with same explicit reason succeeds. | ||
| def test_setUserWriteBlockMode_same_reason_unspecified_idempotent(collection): | ||
| """Test setUserWriteBlockMode re-enable with same reason Unspecified is idempotent.""" | ||
| execute_admin_command( | ||
| collection, | ||
| {"setUserWriteBlockMode": 1, "global": True, "reason": "Unspecified"}, | ||
| ) | ||
| result = execute_admin_command( | ||
| collection, | ||
| {"setUserWriteBlockMode": 1, "global": True, "reason": "Unspecified"}, | ||
| ) | ||
| assertSuccessPartial( | ||
| result, | ||
| {"ok": 1.0}, | ||
| msg="setUserWriteBlockMode should be idempotent with same explicit reason", | ||
| ) | ||
|
|
||
|
|
||
| def test_setUserWriteBlockMode_same_reason_cluster_migration_idempotent(collection): | ||
| """Test setUserWriteBlockMode re-enable with same reason ClusterToClusterMigrationInProgress.""" | ||
| execute_admin_command( | ||
| collection, | ||
| { | ||
| "setUserWriteBlockMode": 1, | ||
| "global": True, | ||
| "reason": "ClusterToClusterMigrationInProgress", | ||
| }, | ||
| ) | ||
| result = execute_admin_command( | ||
| collection, | ||
| { | ||
| "setUserWriteBlockMode": 1, | ||
| "global": True, | ||
| "reason": "ClusterToClusterMigrationInProgress", | ||
| }, | ||
| ) | ||
| assertSuccessPartial( | ||
| result, | ||
| {"ok": 1.0}, | ||
| msg="setUserWriteBlockMode should be idempotent with same explicit reason", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DiskUseThresholdExceeded add this test case well |
||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| """Tests for setUserWriteBlockMode error cases. | ||
|
|
||
| Validates mismatched reason errors when changing the reason on an active block. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import pytest | ||
|
|
||
| from documentdb_tests.compatibility.tests.system.administration.commands.setUserWriteBlockMode.utils.write_block_helpers import ( # noqa: E501 | ||
| force_disable_write_block, | ||
| ) | ||
| from documentdb_tests.framework.assertions import assertFailureCode | ||
| from documentdb_tests.framework.error_codes import ILLEGAL_OPERATION_ERROR | ||
| from documentdb_tests.framework.executor import execute_admin_command | ||
|
|
||
| pytestmark = [pytest.mark.admin, pytest.mark.no_parallel, pytest.mark.requires(cluster_admin=True)] | ||
|
|
||
|
|
||
| @pytest.fixture(autouse=True) | ||
| def _manage_write_block(collection): | ||
| """Ensure write block is disabled before and after each test.""" | ||
| force_disable_write_block(collection) | ||
| yield | ||
| force_disable_write_block(collection) | ||
|
|
||
|
|
||
| # Property [Mismatched Reason on Enable]: re-enabling with a different reason fails. | ||
| def test_setUserWriteBlockMode_enable_mismatched_reason_fails(collection): | ||
| """Test setUserWriteBlockMode re-enable with different reason fails.""" | ||
| execute_admin_command( | ||
| collection, | ||
| {"setUserWriteBlockMode": 1, "global": True, "reason": "Unspecified"}, | ||
| ) | ||
| result = execute_admin_command( | ||
| collection, | ||
| { | ||
| "setUserWriteBlockMode": 1, | ||
| "global": True, | ||
| "reason": "ClusterToClusterMigrationInProgress", | ||
| }, | ||
| ) | ||
| assertFailureCode( | ||
| result, | ||
| ILLEGAL_OPERATION_ERROR, | ||
| msg="setUserWriteBlockMode should reject mismatched reason on re-enable", | ||
| ) | ||
|
|
||
|
|
||
| # Property [Mismatched Reason on Disable]: disabling with a different reason than the active | ||
| # block fails. | ||
| def test_setUserWriteBlockMode_disable_mismatched_reason_fails(collection): | ||
| """Test setUserWriteBlockMode disable with different reason fails.""" | ||
| execute_admin_command( | ||
| collection, | ||
| {"setUserWriteBlockMode": 1, "global": True, "reason": "Unspecified"}, | ||
| ) | ||
| result = execute_admin_command( | ||
| collection, | ||
| { | ||
| "setUserWriteBlockMode": 1, | ||
| "global": False, | ||
| "reason": "ClusterToClusterMigrationInProgress", | ||
| }, | ||
| ) | ||
| assertFailureCode( | ||
| result, | ||
| ILLEGAL_OPERATION_ERROR, | ||
| msg="setUserWriteBlockMode should reject mismatched reason on disable", | ||
| ) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DiskUseThresholdExceeded this here as well |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why twice?