Skip to content
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
Comment thread
alinaliBQ marked this conversation as resolved.
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,141 @@
"""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 and
# writes are allowed.
def test_setUserWriteBlockMode_disable_when_no_block_active(collection):
"""Test setUserWriteBlockMode global:false when no block is active leaves writes allowed."""
execute_admin_command(collection, {"setUserWriteBlockMode": 1, "global": False})
result = execute_command(
collection, {"insert": collection.name, "documents": [{"_id": "no_block_write"}]}
)
assertSuccessPartial(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we additional test with actual write whether its blocking or not

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, added "n": 1 check to ensure insert succeeded and it is not blocking. We also have blocking tests in test_setUserWriteBlockMode_write_block_enforcement.py

result,
{"ok": 1.0, "n": 1},
msg="setUserWriteBlockMode should leave writes allowed when disabling with no active block",
)


# Property [Write Restoration]: writes succeed after disabling a previously active block.
# (Blocking of writes while a block is active is covered by
# test_setUserWriteBlockMode_write_block_enforcement.py.)
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, "n": 1},
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_disk_threshold_idempotent(collection):
"""Test setUserWriteBlockMode re-enable with same reason DiskUseThresholdExceeded."""
execute_admin_command(
collection,
{"setUserWriteBlockMode": 1, "global": True, "reason": "DiskUseThresholdExceeded"},
)
result = execute_admin_command(
collection,
{"setUserWriteBlockMode": 1, "global": True, "reason": "DiskUseThresholdExceeded"},
)
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",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DiskUseThresholdExceeded add this test case well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added test_setUserWriteBlockMode_same_reason_disk_threshold_idempotent test

)
Loading
Loading