From 67ed4a631bb271089a2bd1ea821ce84facdc8480 Mon Sep 17 00:00:00 2001 From: PatersonProjects Date: Thu, 25 Jun 2026 16:07:26 -0700 Subject: [PATCH 1/5] Base Tests Signed-off-by: PatersonProjects --- .../commands/getClusterParameter/__init__.py | 1 + ...t_getClusterParameter_argument_handling.py | 285 ++++++++++++++++++ .../test_getClusterParameter_authorization.py | 44 +++ .../test_getClusterParameter_core_behavior.py | 91 ++++++ ...test_getClusterParameter_name_semantics.py | 158 ++++++++++ ..._getClusterParameter_response_structure.py | 124 ++++++++ .../test_getClusterParameter_round_trip.py | 110 +++++++ .../getClusterParameter/utils/__init__.py | 32 ++ 8 files changed, 845 insertions(+) create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/__init__.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/utils/__init__.py diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/__init__.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/__init__.py new file mode 100644 index 000000000..e07018905 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/__init__.py @@ -0,0 +1 @@ +"""getClusterParameter command tests.""" diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py new file mode 100644 index 000000000..b936bf427 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py @@ -0,0 +1,285 @@ +"""Tests for getClusterParameter argument and command-document handling. + +Covers the parameter argument's accepted/rejected BSON types, the three +documented argument forms (single name / array / wildcard), array-form edge +cases, null/empty-string coercion, and command-document quirks (comment field, +case-sensitive command key, unrecognized fields). + +Parameter values vary by deployment, so success cases assert on response +structure (ok, clusterParameters length/type) and never on exact values. +""" + +from dataclasses import dataclass +from typing import Any + +import pytest + +from documentdb_tests.compatibility.tests.system.administration.commands.getClusterParameter.utils import ( # noqa: E501 + valid_parameter_names, +) +from documentdb_tests.framework.assertions import ( + assertFailureCode, + assertProperties, + assertSuccessPartial, +) +from documentdb_tests.framework.bson_type_validator import ( + BsonTypeTestCase, + generate_bson_rejection_test_cases, +) +from documentdb_tests.framework.error_codes import ( + BAD_VALUE_ERROR, + COMMAND_NOT_FOUND_ERROR, + NO_SUCH_KEY_ERROR, + TYPE_MISMATCH_ERROR, +) +from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.property_checks import Eq, Gt, IsType, Len +from documentdb_tests.framework.test_case import BaseTestCase +from documentdb_tests.framework.test_constants import BsonType + +pytestmark = pytest.mark.admin + + +# --------------------------------------------------------------------------- +# Category #1 / #3: argument BSON-type rejection +# The argument must be a string (single name or '*') or an array of strings. +# Every other BSON type is rejected with a type-mismatch error. NULL is handled +# separately in the coercion tests below. +# --------------------------------------------------------------------------- + +_ARGUMENT_TYPE_SPEC = [ + BsonTypeTestCase( + id="getClusterParameter_argument", + msg="getClusterParameter should reject non-string/non-array argument types", + keyword="getClusterParameter", + valid_types=[BsonType.STRING, BsonType.ARRAY], + default_error_code=TYPE_MISMATCH_ERROR, + skip_rejection_types=[BsonType.NULL], + ), +] + +_ARGUMENT_REJECTION_CASES = generate_bson_rejection_test_cases(_ARGUMENT_TYPE_SPEC) + + +@pytest.mark.parametrize("bson_type,sample_value,spec", _ARGUMENT_REJECTION_CASES) +def test_getClusterParameter_argument_rejects_type(collection, bson_type, sample_value, spec): + """Test getClusterParameter rejects non-string/non-array argument types.""" + result = execute_admin_command(collection, {"getClusterParameter": sample_value}) + assertFailureCode( + result, + spec.expected_code(bson_type), + msg=f"getClusterParameter should reject {bson_type.value} argument.", + ) + + +# --------------------------------------------------------------------------- +# Category #2 / #10: documented argument forms and array shapes +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_single_name_returns_one(collection): + """Test a single parameter name returns exactly one entry.""" + (name,) = valid_parameter_names(collection, 1) + result = execute_admin_command(collection, {"getClusterParameter": name}) + assertProperties( + result, + {"ok": Eq(1.0), "clusterParameters": Len(1)}, + msg="Single name should return exactly one clusterParameters entry.", + raw_res=True, + ) + + +def test_getClusterParameter_array_single_name_returns_one(collection): + """Test a one-element array behaves like the single-name form.""" + names = valid_parameter_names(collection, 1) + result = execute_admin_command(collection, {"getClusterParameter": names}) + assertProperties( + result, + {"ok": Eq(1.0), "clusterParameters": Len(1)}, + msg="One-element array should return exactly one entry.", + raw_res=True, + ) + + +def test_getClusterParameter_array_two_names_returns_two(collection): + """Test an array of two valid names returns two entries.""" + names = valid_parameter_names(collection, 2) + result = execute_admin_command(collection, {"getClusterParameter": names}) + assertProperties( + result, + {"ok": Eq(1.0), "clusterParameters": Len(2)}, + msg="Array of two names should return two entries.", + raw_res=True, + ) + + +def test_getClusterParameter_array_three_names_returns_three(collection): + """Test an array of three valid names returns three entries.""" + names = valid_parameter_names(collection, 3) + result = execute_admin_command(collection, {"getClusterParameter": names}) + assertProperties( + result, + {"ok": Eq(1.0), "clusterParameters": Len(3)}, + msg="Array of three names should return three entries.", + raw_res=True, + ) + + +def test_getClusterParameter_wildcard_succeeds(collection): + """Test the '*' wildcard returns an array of parameters with ok:1.""" + result = execute_admin_command(collection, {"getClusterParameter": "*"}) + assertProperties( + result, + {"ok": Eq(1.0), "clusterParameters": IsType("array")}, + msg="'*' should return a clusterParameters array with ok:1.", + raw_res=True, + ) + + +def test_getClusterParameter_wildcard_is_non_empty(collection): + """Test the '*' wildcard returns at least one parameter.""" + result = execute_admin_command(collection, {"getClusterParameter": "*"}) + count = len(result["clusterParameters"]) + assertProperties( + {"count": count}, + {"count": Gt(0)}, + msg=f"'*' should return at least one parameter (got {count}).", + raw_res=True, + ) + + +def test_getClusterParameter_duplicate_names(collection): + """Test an array with a duplicated valid name is accepted.""" + names = valid_parameter_names(collection, 1) + result = execute_admin_command(collection, {"getClusterParameter": [names[0], names[0]]}) + assertSuccessPartial( + result, + {"ok": 1.0}, + msg="Array with a duplicated valid name should be accepted.", + ) + + +def test_getClusterParameter_many_names_no_truncation(collection): + """Test a large array of names is handled without truncation or failure.""" + names = valid_parameter_names(collection, 1) + big = [names[0]] * 100 + result = execute_admin_command(collection, {"getClusterParameter": big}) + assertSuccessPartial( + result, + {"ok": 1.0}, + msg="Large array of names should succeed without truncation.", + ) + + +# --------------------------------------------------------------------------- +# Argument / array-element error cases (parametrized) +# --------------------------------------------------------------------------- + + +@dataclass(frozen=True) +class ArgumentErrorCase(BaseTestCase): + """An argument value expected to produce a specific error code.""" + + argument: Any = None + + +_ARGUMENT_ERROR_CASES: list[ArgumentErrorCase] = [ + ArgumentErrorCase( + "array_with_int_element", + argument=["validNamePlaceholder", 123], + error_code=TYPE_MISMATCH_ERROR, + msg="Array with a non-string (int) element should be rejected.", + ), + ArgumentErrorCase( + "nested_array_element", + argument=[["validNamePlaceholder"]], + error_code=TYPE_MISMATCH_ERROR, + msg="Array with a nested-array element should be rejected.", + ), + ArgumentErrorCase( + "object_element", + argument=[{"x": 1}], + error_code=TYPE_MISMATCH_ERROR, + msg="Array with a document element should be rejected.", + ), +] + + +@pytest.mark.parametrize("test", pytest_params(_ARGUMENT_ERROR_CASES)) +def test_getClusterParameter_array_element_rejects_type(collection, test): + """Test array elements that are not strings are rejected.""" + result = execute_admin_command(collection, {"getClusterParameter": test.argument}) + assertFailureCode(result, test.error_code, msg=test.msg) + + +# --------------------------------------------------------------------------- +# Category #6: null / empty-string coercion of the argument +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_null_argument(collection): + """Test a null argument is rejected with a type-mismatch error.""" + result = execute_admin_command(collection, {"getClusterParameter": None}) + assertFailureCode( + result, + TYPE_MISMATCH_ERROR, + msg="Null argument should be rejected as a type mismatch.", + ) + + +def test_getClusterParameter_empty_string_argument(collection): + """Test an empty-string argument is treated as an unknown parameter name.""" + result = execute_admin_command(collection, {"getClusterParameter": ""}) + assertFailureCode( + result, + NO_SUCH_KEY_ERROR, + msg="Empty-string argument should be treated as an unknown parameter name.", + ) + + +def test_getClusterParameter_empty_array_argument(collection): + """Test an empty array does not return all parameters.""" + result = execute_admin_command(collection, {"getClusterParameter": []}) + assertFailureCode( + result, + BAD_VALUE_ERROR, + msg="Empty array should not return all parameters.", + ) + + +# --------------------------------------------------------------------------- +# Category #15: command-document quirks +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_comment_accepted(collection): + """Test the optional comment field is accepted and does not affect success.""" + result = execute_admin_command( + collection, {"getClusterParameter": "*", "comment": "functional-test"} + ) + assertSuccessPartial( + result, + {"ok": 1.0}, + msg="comment field should be accepted.", + ) + + +def test_getClusterParameter_unrecognized_field_ignored(collection): + """Test an unrecognized top-level field is ignored rather than rejected.""" + result = execute_admin_command(collection, {"getClusterParameter": "*", "bogusField": 1}) + assertSuccessPartial( + result, + {"ok": 1.0}, + msg="Unrecognized top-level field should be ignored.", + ) + + +def test_getClusterParameter_wrong_case_command_key(collection): + """Test the command key is case-sensitive (getclusterparameter is unknown).""" + result = execute_admin_command(collection, {"getclusterparameter": "*"}) + assertFailureCode( + result, + COMMAND_NOT_FOUND_ERROR, + msg="Lower-case command key should be an unknown command.", + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py new file mode 100644 index 000000000..feb0696f2 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py @@ -0,0 +1,44 @@ +"""Tests for getClusterParameter authorization and namespace enforcement. + +getClusterParameter is an admin-only command guarded by the +``getClusterParameter`` privilege action. The cases that run without +authentication are covered here: the command is rejected against any +non-admin database (namespace delegation), and it succeeds on admin when auth +is disabled. + +DEFERRED (require an auth-enabled environment, not available on this target): +- A user holding the getClusterParameter action succeeds (#5, #7). +- A user lacking the action fails with an authorization error (#3, #5, #7). +- The read (getClusterParameter) action without the setClusterParameter action + still permits reads (#18). +- A role scoped to a non-admin database does not grant this cluster-scoped + action (#18). +""" + +import pytest + +from documentdb_tests.framework.assertions import assertFailureCode, assertSuccessPartial +from documentdb_tests.framework.error_codes import UNAUTHORIZED_ERROR +from documentdb_tests.framework.executor import execute_admin_command, execute_command + +pytestmark = [pytest.mark.admin, pytest.mark.rbac] + + +def test_getClusterParameter_rejected_on_non_admin_database(collection): + """Test getClusterParameter is rejected against a non-admin database.""" + result = execute_command(collection, {"getClusterParameter": "*"}) + assertFailureCode( + result, + UNAUTHORIZED_ERROR, + msg="getClusterParameter should be rejected on a non-admin database.", + ) + + +def test_getClusterParameter_succeeds_on_admin_without_auth(collection): + """Test getClusterParameter succeeds on admin when authentication is disabled.""" + result = execute_admin_command(collection, {"getClusterParameter": "*"}) + assertSuccessPartial( + result, + {"ok": 1.0}, + msg="getClusterParameter should succeed on admin with auth disabled.", + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py new file mode 100644 index 000000000..27c3aad36 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py @@ -0,0 +1,91 @@ +"""Tests for getClusterParameter core retrieval behavior. + +Covers read coherence and idempotency that hold on any single deployment: +repeated reads are stable, a single-name read agrees with the same parameter's +entry in the wildcard result, and every parameter advertised by '*' is +individually retrievable. + +DEFERRED (require a sharded cluster or controlled concurrency, not available on +a single mongod target): +- mongos durable value vs mongod cached value divergence and eventual + convergence (#13). On a sharded cluster mongos reads the config-server + durable value while mongod reads a cached snapshot; this needs a mongos + + shard topology to observe. +- Concurrent getClusterParameter during a setClusterParameter must observe a + coherent (old or new) snapshot, never partial (#14). Needs deterministic + interleaving with a writer and is inherently racy on a single node. +""" + +import pytest + +from documentdb_tests.compatibility.tests.system.administration.commands.getClusterParameter.utils import ( # noqa: E501 + all_cluster_parameters, + valid_parameter_names, +) +from documentdb_tests.framework.assertions import assertProperties +from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.property_checks import Eq + +pytestmark = pytest.mark.admin + + +def test_getClusterParameter_repeated_wildcard_returns_same_parameter_set(collection): + """Test repeated '*' calls return the same set of parameter names (idempotent).""" + first = execute_admin_command(collection, {"getClusterParameter": "*"})["clusterParameters"] + second = execute_admin_command(collection, {"getClusterParameter": "*"})["clusterParameters"] + names_first = sorted(p["_id"] for p in first) + names_second = sorted(p["_id"] for p in second) + assertProperties( + {"names": names_second}, + {"names": Eq(names_first)}, + msg="Repeated '*' reads should return the same set of parameter names.", + raw_res=True, + ) + + +def test_getClusterParameter_repeated_single_read_is_consistent(collection): + """Test reading the same parameter twice returns an identical entry.""" + (name,) = valid_parameter_names(collection, 1) + first = execute_admin_command(collection, {"getClusterParameter": name})["clusterParameters"][0] + second = execute_admin_command(collection, {"getClusterParameter": name})["clusterParameters"][ + 0 + ] + assertProperties( + {"entry": second}, + {"entry": Eq(first)}, + msg=f"Repeated single read of '{name}' should be consistent.", + raw_res=True, + ) + + +def test_getClusterParameter_single_name_matches_wildcard_entry(collection): + """Test a single-name read agrees with that parameter's entry under '*'.""" + (name,) = valid_parameter_names(collection, 1) + single = execute_admin_command(collection, {"getClusterParameter": name})["clusterParameters"][ + 0 + ] + wildcard_entry = next(p for p in all_cluster_parameters(collection) if p["_id"] == name) + assertProperties( + {"entry": single}, + {"entry": Eq(wildcard_entry)}, + msg=f"Single read of '{name}' should match its '*' entry.", + raw_res=True, + ) + + +def test_getClusterParameter_wildcard_names_individually_retrievable(collection): + """Test parameters advertised by '*' are each retrievable by name.""" + names = [p["_id"] for p in all_cluster_parameters(collection)][:5] + retrieved = {} + for name in names: + params = execute_admin_command(collection, {"getClusterParameter": name})[ + "clusterParameters" + ] + retrieved[name] = [len(params), params[0]["_id"] if params else None] + expected = {name: [1, name] for name in names} + assertProperties( + {"retrieved": retrieved}, + {"retrieved": Eq(expected)}, + msg="Each parameter from '*' should be individually retrievable by name.", + raw_res=True, + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py new file mode 100644 index 000000000..f07386aad --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py @@ -0,0 +1,158 @@ +"""Tests for how getClusterParameter interprets parameter names. + +Parameter names are exact-match literals: there is no field-path traversal, +operator interpretation, globbing, or case folding. Any name that is not an +exact match for an existing cluster parameter is reported as unknown, and the +single-name and array forms report an unknown name identically. + +(The empty-string name case is covered in test_getClusterParameter_argument_handling.py.) +""" + +from dataclasses import dataclass +from typing import Any + +import pytest + +from documentdb_tests.compatibility.tests.system.administration.commands.getClusterParameter.utils import ( # noqa: E501 + valid_parameter_names, +) +from documentdb_tests.framework.assertions import assertFailureCode +from documentdb_tests.framework.error_codes import NO_SUCH_KEY_ERROR +from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_case import BaseTestCase + +pytestmark = pytest.mark.admin + +_UNKNOWN_NAME = "definitelyNotAClusterParameterXYZ" + + +@dataclass(frozen=True) +class NameCase(BaseTestCase): + """A parameter-name argument expected to be reported as unknown.""" + + argument: Any = None + + +# --------------------------------------------------------------------------- +# Category #8: argument boundary probing — every odd name is a literal, +# unknown parameter name (no traversal/operator/glob interpretation). +# Category #9: wildcard edges — '*' is only special as the whole-string +# argument; inside an array or combined with other characters it is literal. +# Category #11: an unknown name reports identically as a string or in an array. +# --------------------------------------------------------------------------- + +_UNKNOWN_NAME_CASES: list[NameCase] = [ + # #8 boundary probing + NameCase( + "whitespace_only", + argument=" ", + error_code=NO_SUCH_KEY_ERROR, + msg="Whitespace-only name should be an unknown parameter.", + ), + NameCase( + "very_long_10k", + argument="x" * 10000, + error_code=NO_SUCH_KEY_ERROR, + msg="A 10k-character name should be an unknown parameter (no crash).", + ), + NameCase( + "dotted_name", + argument="a.b.c", + error_code=NO_SUCH_KEY_ERROR, + msg="Dotted name should be literal, not a field path -> unknown parameter.", + ), + NameCase( + "dollar_prefixed", + argument="$foo", + error_code=NO_SUCH_KEY_ERROR, + msg="$-prefixed name should be literal, not an operator -> unknown parameter.", + ), + NameCase( + "unicode_name", + argument="paramètre_café_\u6d4b\u8bd5", + error_code=NO_SUCH_KEY_ERROR, + msg="Unicode name should be an unknown parameter.", + ), + # #9 wildcard edges + NameCase( + "star_in_array", + argument=["*"], + error_code=NO_SUCH_KEY_ERROR, + msg="'*' inside an array should be a literal name, not expand-all.", + ), + NameCase( + "double_star", + argument="**", + error_code=NO_SUCH_KEY_ERROR, + msg="'**' should be a literal name (no globbing).", + ), + NameCase( + "star_prefix", + argument="*foo", + error_code=NO_SUCH_KEY_ERROR, + msg="'*foo' should be a literal name (no globbing).", + ), + # #11 unknown-name contract + NameCase( + "unknown_single_string", + argument=_UNKNOWN_NAME, + error_code=NO_SUCH_KEY_ERROR, + msg="An unknown single name should report NoSuchKey.", + ), + NameCase( + "unknown_in_array", + argument=[_UNKNOWN_NAME], + error_code=NO_SUCH_KEY_ERROR, + msg="An unknown name inside an array should report NoSuchKey.", + ), +] + + +@pytest.mark.parametrize("test", pytest_params(_UNKNOWN_NAME_CASES)) +def test_getClusterParameter_unknown_name(collection, test): + """Test odd/unknown parameter names are reported as unknown.""" + result = execute_admin_command(collection, {"getClusterParameter": test.argument}) + assertFailureCode(result, test.error_code, msg=test.msg) + + +# --------------------------------------------------------------------------- +# Cases needing a deployment-derived valid name (built at runtime). +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_name_is_case_sensitive(collection): + """Test a known name with altered case is treated as unknown (exact match).""" + (name,) = valid_parameter_names(collection, 1) + altered = name.upper() if name != name.upper() else name.lower() + result = execute_admin_command(collection, {"getClusterParameter": altered}) + assertFailureCode( + result, + NO_SUCH_KEY_ERROR, + msg=f"Case-altered name '{altered}' should not match known name '{name}'.", + ) + + +def test_getClusterParameter_star_with_valid_name_in_array(collection): + """Test '*' combined with a valid name in an array treats '*' literally.""" + (name,) = valid_parameter_names(collection, 1) + result = execute_admin_command(collection, {"getClusterParameter": ["*", name]}) + assertFailureCode( + result, + NO_SUCH_KEY_ERROR, + msg="'*' alongside a valid name in an array should be a literal unknown name.", + ) + + +def test_getClusterParameter_unknown_name_contract_consistent(collection): + """Test single-string and array forms report an unknown name identically.""" + single = execute_admin_command(collection, {"getClusterParameter": _UNKNOWN_NAME}) + array = execute_admin_command(collection, {"getClusterParameter": [_UNKNOWN_NAME]}) + single_code = getattr(single, "code", None) + array_code = getattr(array, "code", None) + assertFailureCode( + array, + single_code, + msg=f"Array unknown-name code ({array_code}) should match single " + f"unknown-name code ({single_code}).", + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py new file mode 100644 index 000000000..2b12dd48c --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py @@ -0,0 +1,124 @@ +"""Tests for getClusterParameter response structure and value-type fidelity. + +The top-level shape ({ok:1, clusterParameters: }) is asserted in +test_getClusterParameter_argument_handling.py; this file covers element-level +structure and BSON-type fidelity: each element is an object keyed by ``_id``, +a single request isolates exactly the requested name, and parameter values +(nested documents, numbers, and clusterParameterTime) keep their BSON types +without coercion. +""" + +import pytest +from bson import Decimal128, Int64, Timestamp + +from documentdb_tests.framework.assertions import assertProperties +from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.property_checks import Eq, Len + +pytestmark = pytest.mark.admin + +# Numeric BSON types that must survive a round trip without being coerced to +# string. bool is intentionally excluded (it is not a numeric value here). +_NUMERIC_TYPES = (int, float, Int64, Decimal128) + + +def test_getClusterParameter_each_element_is_object_with_string_id(collection): + """Test every clusterParameters element is an object with a string _id.""" + params = execute_admin_command(collection, {"getClusterParameter": "*"})["clusterParameters"] + summary = { + "all_objects": all(isinstance(e, dict) for e in params), + "all_ids_str": all(isinstance(e.get("_id"), str) for e in params), + } + assertProperties( + {"summary": summary}, + {"summary": Eq({"all_objects": True, "all_ids_str": True})}, + msg="Every clusterParameters element should be an object with a string _id.", + raw_res=True, + ) + + +def test_getClusterParameter_single_request_isolates_name(collection): + """Test requesting one name returns exactly that name and no others.""" + name = execute_admin_command(collection, {"getClusterParameter": "*"})["clusterParameters"][0][ + "_id" + ] + result = execute_admin_command(collection, {"getClusterParameter": name}) + assertProperties( + result, + {"clusterParameters": Len(1), "clusterParameters.0._id": Eq(name)}, + msg=f"Requesting '{name}' should return exactly that name.", + raw_res=True, + ) + + +def test_getClusterParameter_clusterParameterTime_is_timestamp_when_present(collection): + """Test any clusterParameterTime field is a BSON Timestamp.""" + params = execute_admin_command(collection, {"getClusterParameter": "*"})["clusterParameters"] + times = [e["clusterParameterTime"] for e in params if "clusterParameterTime" in e] + if not times: + pytest.skip("no parameter on this deployment carries clusterParameterTime") + all_timestamps = all(isinstance(t, Timestamp) for t in times) + assertProperties( + {"all_timestamps": all_timestamps}, + {"all_timestamps": Eq(True)}, + msg="clusterParameterTime should be a BSON Timestamp.", + raw_res=True, + ) + + +def test_getClusterParameter_document_valued_parameter_preserves_nested_object(collection): + """Test a document-valued parameter keeps its nested object structure.""" + params = execute_admin_command(collection, {"getClusterParameter": "*"})["clusterParameters"] + nested_is_object = None + for entry in params: + for key, value in entry.items(): + if key != "_id" and isinstance(value, dict): + nested_is_object = isinstance(value, dict) + break + if nested_is_object is not None: + break + if nested_is_object is None: + pytest.skip("no document-valued cluster parameter on this deployment") + assertProperties( + {"nested_is_object": nested_is_object}, + {"nested_is_object": Eq(True)}, + msg="A document-valued parameter should preserve its nested object structure.", + raw_res=True, + ) + + +def test_getClusterParameter_numeric_value_not_coerced(collection): + """Test numeric parameter values keep a numeric BSON type (not stringified).""" + params = execute_admin_command(collection, {"getClusterParameter": "*"})["clusterParameters"] + + def first_numeric_type(value): + if isinstance(value, bool): + return None + if isinstance(value, _NUMERIC_TYPES): + return type(value).__name__ + if isinstance(value, dict): + for v in value.values(): + found = first_numeric_type(v) + if found: + return found + return None + + numeric_type = None + for entry in params: + for key, value in entry.items(): + if key == "_id": + continue + numeric_type = first_numeric_type(value) + if numeric_type: + break + if numeric_type: + break + if numeric_type is None: + pytest.skip("no numeric-valued cluster parameter on this deployment") + is_numeric_type = numeric_type in {"int", "float", "Int64", "Decimal128"} + assertProperties( + {"is_numeric_type": is_numeric_type}, + {"is_numeric_type": Eq(True)}, + msg=f"Numeric parameter value kept numeric BSON type '{numeric_type}' (not coerced).", + raw_res=True, + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py new file mode 100644 index 000000000..786426043 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py @@ -0,0 +1,110 @@ +"""Tests for getClusterParameter cross-command interactions. + +Round-trip (#12): a value written with setClusterParameter is read back by +getClusterParameter, and a never-set parameter returns its default without +error. + +Differential (#16): getClusterParameter and getParameter occupy distinct +namespaces (a cluster parameter is not retrievable via getParameter and a +server parameter is not a cluster parameter), and getDefaultRWConcern is a +separate facility (defaultRWConcern is not a cluster parameter). + +Marked no_parallel because the round-trip tests mutate a cluster parameter; +each restores the original value in a finally block. + +DEFERRED (require a sharded cluster, not this single target): +- After a set, mongos durable value and mongod cached value converge + eventually (#12). +- A settable-but-internal parameter's presence under '*' vs setClusterParameter + acceptance (#12) — needs an internal parameter inventory. +""" + +import pytest + +from documentdb_tests.framework.assertions import assertFailureCode, assertProperties +from documentdb_tests.framework.error_codes import INVALID_OPTIONS_ERROR +from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.property_checks import Eq, IsType + +pytestmark = [pytest.mark.admin, pytest.mark.no_parallel] + + +def test_getClusterParameter_set_then_get_reflects_value(collection): + """Test a value written by setClusterParameter is read back (read-after-write).""" + get0 = execute_admin_command(collection, {"getClusterParameter": "defaultMaxTimeMS"}) + original = int(get0["clusterParameters"][0]["readOperations"]) + new_value = 4242 if original != 4242 else 1234 + try: + execute_admin_command( + collection, + {"setClusterParameter": {"defaultMaxTimeMS": {"readOperations": new_value}}}, + ) + got = execute_admin_command(collection, {"getClusterParameter": "defaultMaxTimeMS"}) + read_back = int(got["clusterParameters"][0]["readOperations"]) + assertProperties( + {"read_back": read_back}, + {"read_back": Eq(new_value)}, + msg="getClusterParameter should read back the value set by setClusterParameter.", + raw_res=True, + ) + finally: + execute_admin_command( + collection, + {"setClusterParameter": {"defaultMaxTimeMS": {"readOperations": original}}}, + ) + + +def test_getClusterParameter_never_set_returns_default(collection): + """Test a parameter that was not explicitly set still returns a default value.""" + result = execute_admin_command(collection, {"getClusterParameter": "defaultMaxTimeMS"}) + assertProperties( + result, + {"ok": Eq(1.0), "clusterParameters.0.readOperations": IsType("long")}, + msg="A never-set parameter should return its default without error.", + raw_res=True, + ) + + +def test_getClusterParameter_cluster_param_not_retrievable_via_getParameter(collection): + """Test a cluster parameter name is not retrievable through getParameter.""" + name = execute_admin_command(collection, {"getClusterParameter": "*"})["clusterParameters"][0][ + "_id" + ] + result = execute_admin_command(collection, {"getParameter": 1, name: 1}) + assertFailureCode( + result, + INVALID_OPTIONS_ERROR, + msg=f"Cluster parameter '{name}' should not be retrievable via getParameter.", + ) + + +def test_getClusterParameter_server_parameter_not_a_cluster_parameter(collection): + """Test a server parameter (logLevel) is not present in cluster parameters.""" + names = [ + p["_id"] + for p in execute_admin_command(collection, {"getClusterParameter": "*"})[ + "clusterParameters" + ] + ] + assertProperties( + {"has_logLevel": "logLevel" in names}, + {"has_logLevel": Eq(False)}, + msg="Server parameter 'logLevel' should not be a cluster parameter.", + raw_res=True, + ) + + +def test_getClusterParameter_defaultRWConcern_not_a_cluster_parameter(collection): + """Test defaultRWConcern is not a cluster parameter (distinct from getDefaultRWConcern).""" + names = [ + p["_id"] + for p in execute_admin_command(collection, {"getClusterParameter": "*"})[ + "clusterParameters" + ] + ] + assertProperties( + {"has_defaultRWConcern": "defaultRWConcern" in names}, + {"has_defaultRWConcern": Eq(False)}, + msg="defaultRWConcern should not appear among cluster parameters.", + raw_res=True, + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/utils/__init__.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/utils/__init__.py new file mode 100644 index 000000000..940510b19 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/utils/__init__.py @@ -0,0 +1,32 @@ +"""Shared helpers for getClusterParameter tests. + +Cluster parameter values and the exact set of available parameters vary by +deployment, so tests derive a valid parameter name at runtime from the +wildcard form rather than hard-coding one. +""" + +from documentdb_tests.framework.executor import execute_admin_command + + +def all_cluster_parameters(collection): + """Return the ``clusterParameters`` list from ``getClusterParameter: '*'``. + + Raises the underlying exception if the command did not succeed, so callers + fail loudly during setup rather than asserting against an error object. + """ + result = execute_admin_command(collection, {"getClusterParameter": "*"}) + if isinstance(result, Exception): + raise result + return result["clusterParameters"] + + +def valid_parameter_names(collection, count=1): + """Return up to ``count`` valid cluster parameter names for this deployment.""" + params = all_cluster_parameters(collection) + names = [p["_id"] for p in params] + if len(names) < count: + raise AssertionError( + f"deployment exposes only {len(names)} cluster parameter(s), " + f"need {count} for this test" + ) + return names[:count] From b3f1508f35b1b2522569a229a153d9bd579925e2 Mon Sep 17 00:00:00 2001 From: PatersonProjects Date: Fri, 26 Jun 2026 11:12:03 -0700 Subject: [PATCH 2/5] Base Tests Signed-off-by: PatersonProjects --- .../commands/getClusterParameter/__init__.py | 0 ...t_getClusterParameter_argument_handling.py | 228 ++++++++++++++++++ .../test_getClusterParameter_authorization.py | 58 +++++ .../test_getClusterParameter_core_behavior.py | 111 +++++++++ ...test_getClusterParameter_name_semantics.py | 149 ++++++++++++ ..._getClusterParameter_response_structure.py | 101 ++++++++ .../test_getClusterParameter_round_trip.py | 92 +++++++ 7 files changed, 739 insertions(+) create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/__init__.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/__init__.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py new file mode 100644 index 000000000..188bd65c3 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py @@ -0,0 +1,228 @@ +"""Tests for getClusterParameter argument handling. + +Covers BSON type rejection/acceptance for the argument, +argument forms (single/array/wildcard/empty), undocumented coercion +edge cases, array-form edge cases, and command-document quirks. + +Categories: #1, #2, #3 (type/field portions), #6, #10, #15 +""" + +from dataclasses import dataclass +from datetime import datetime, timezone +from typing import Any + +import pytest +from bson import Binary, Code, Decimal128, Int64, MaxKey, MinKey, ObjectId, Regex, Timestamp + +from documentdb_tests.framework.assertions import assertFailureCode, assertSuccessPartial +from documentdb_tests.framework.error_codes import ( + BAD_VALUE_ERROR, + COMMAND_NOT_FOUND_ERROR, + NO_SUCH_KEY_ERROR, + TYPE_MISMATCH_ERROR, +) +from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_case import BaseTestCase + +pytestmark = pytest.mark.admin + +_VALID_PARAM = "changeStreamOptions" +_VALID_PARAM_2 = "changeStreams" + + +# --------------------------------------------------------------------------- +# §1 / §6 BSON type rejection for non-string / non-array argument types +# --------------------------------------------------------------------------- + + +@dataclass(frozen=True) +class TypeCase(BaseTestCase): + """Test case for type handling.""" + + arg: Any = None + + +_REJECT_TYPES: list[TypeCase] = [ + TypeCase("int", arg=1, error_code=TYPE_MISMATCH_ERROR, msg="Should reject int"), + TypeCase("int64", arg=Int64(1), error_code=TYPE_MISMATCH_ERROR, msg="Should reject Int64"), + TypeCase("double", arg=3.14, error_code=TYPE_MISMATCH_ERROR, msg="Should reject double"), + TypeCase( + "decimal128", + arg=Decimal128("1"), + error_code=TYPE_MISMATCH_ERROR, + msg="Should reject Decimal128", + ), + TypeCase("bool_true", arg=True, error_code=TYPE_MISMATCH_ERROR, msg="Should reject bool true"), + TypeCase( + "bool_false", arg=False, error_code=TYPE_MISMATCH_ERROR, msg="Should reject bool false" + ), + TypeCase("object", arg={"a": 1}, error_code=TYPE_MISMATCH_ERROR, msg="Should reject object"), + TypeCase( + "bindata", + arg=Binary(b"x"), + error_code=TYPE_MISMATCH_ERROR, + msg="Should reject binData", + ), + TypeCase( + "objectid", + arg=ObjectId("000000000000000000000000"), + error_code=TYPE_MISMATCH_ERROR, + msg="Should reject ObjectId", + ), + TypeCase( + "date", + arg=datetime(2024, 1, 1, tzinfo=timezone.utc), + error_code=TYPE_MISMATCH_ERROR, + msg="Should reject date", + ), + TypeCase("regex", arg=Regex("x"), error_code=TYPE_MISMATCH_ERROR, msg="Should reject regex"), + TypeCase( + "javascript", + arg=Code("function(){}"), + error_code=TYPE_MISMATCH_ERROR, + msg="Should reject JavaScript", + ), + TypeCase( + "timestamp", + arg=Timestamp(0, 1), + error_code=TYPE_MISMATCH_ERROR, + msg="Should reject Timestamp", + ), + TypeCase("minkey", arg=MinKey(), error_code=TYPE_MISMATCH_ERROR, msg="Should reject MinKey"), + TypeCase("maxkey", arg=MaxKey(), error_code=TYPE_MISMATCH_ERROR, msg="Should reject MaxKey"), + TypeCase("null", arg=None, error_code=TYPE_MISMATCH_ERROR, msg="Should reject null"), +] + + +@pytest.mark.parametrize("test", pytest_params(_REJECT_TYPES)) +def test_getClusterParameter_rejects_invalid_bson_type(collection, test): + """Test getClusterParameter rejects non-string/non-array argument types.""" + result = execute_admin_command(collection, {"getClusterParameter": test.arg}) + assertFailureCode(result, test.error_code, msg=test.msg) + + +# --------------------------------------------------------------------------- +# §2 Argument forms — accepted +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_wildcard_returns_all(collection): + """Test '*' argument returns all available cluster parameters with ok:1.""" + result = execute_admin_command(collection, {"getClusterParameter": "*"}) + assertSuccessPartial(result, {"ok": 1.0}, msg="Wildcard '*' should return ok:1") + + +def test_getClusterParameter_single_name_returns_one(collection): + """Test single string name returns exactly one clusterParameters entry.""" + result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) + from documentdb_tests.framework.assertions import assertProperties + from documentdb_tests.framework.property_checks import Eq, Len + + assertProperties( + result, + {"ok": Eq(1.0), "clusterParameters": Len(1)}, + msg="Single name should return ok:1 with one parameter", + raw_res=True, + ) + + +def test_getClusterParameter_array_two_names_returns_two(collection): + """Test array of two valid names returns two clusterParameters entries.""" + result = execute_admin_command( + collection, {"getClusterParameter": [_VALID_PARAM, _VALID_PARAM_2]} + ) + from documentdb_tests.framework.assertions import assertProperties + from documentdb_tests.framework.property_checks import Eq, Len + + assertProperties( + result, + {"ok": Eq(1.0), "clusterParameters": Len(2)}, + msg="Array of two names should return two parameters", + raw_res=True, + ) + + +def test_getClusterParameter_array_duplicate_names(collection): + """Test array with duplicate valid names succeeds.""" + result = execute_admin_command( + collection, {"getClusterParameter": [_VALID_PARAM, _VALID_PARAM]} + ) + assertSuccessPartial(result, {"ok": 1.0}, msg="Duplicate names in array should succeed") + + +# --------------------------------------------------------------------------- +# §2 Argument forms — rejected +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_empty_array_errors(collection): + """Test empty array argument fails with BAD_VALUE_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": []}) + assertFailureCode(result, BAD_VALUE_ERROR, msg="Empty array must supply at least one name") + + +def test_getClusterParameter_array_nonstring_element_rejects(collection): + """Test array containing a non-string element fails with TYPE_MISMATCH_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": [_VALID_PARAM, 123]}) + assertFailureCode( + result, TYPE_MISMATCH_ERROR, msg="Non-string array element should be rejected" + ) + + +def test_getClusterParameter_unknown_name_errors(collection): + """Test an unknown parameter name fails with NO_SUCH_KEY_ERROR (code 4).""" + result = execute_admin_command(collection, {"getClusterParameter": "doesNotExist"}) + assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="Unknown parameter name should fail") + + +def test_getClusterParameter_unrecognized_field_accepted(collection): + """Test extra comment field is accepted (MongoDB treats it as generic command field).""" + result = execute_admin_command(collection, {"getClusterParameter": "*", "comment": "test"}) + assertSuccessPartial(result, {"ok": 1.0}, msg="comment field should be accepted") + + +# --------------------------------------------------------------------------- +# §6 Undocumented coercion edge cases +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_array_null_element_rejects(collection): + """Test array containing null element fails with TYPE_MISMATCH_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": [None]}) + assertFailureCode(result, TYPE_MISMATCH_ERROR, msg="Null element in array should be rejected") + + +def test_getClusterParameter_array_doc_element_rejects(collection): + """Test array containing a document element fails with TYPE_MISMATCH_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": [{"a": 1}]}) + assertFailureCode( + result, TYPE_MISMATCH_ERROR, msg="Document element in array should be rejected" + ) + + +def test_getClusterParameter_array_nested_array_rejects(collection): + """Test array containing a nested array element fails with TYPE_MISMATCH_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": [[_VALID_PARAM]]}) + assertFailureCode(result, TYPE_MISMATCH_ERROR, msg="Nested array element should be rejected") + + +def test_getClusterParameter_array_mixed_valid_unknown_errors(collection): + """Test array with one valid and one unknown name fails with NO_SUCH_KEY_ERROR.""" + result = execute_admin_command( + collection, {"getClusterParameter": [_VALID_PARAM, "unknownParam"]} + ) + assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="Unknown entry in mixed array should fail") + + +# --------------------------------------------------------------------------- +# §15 Command-document quirks +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_wrong_case_command_key_rejected(collection): + """Test wrong-case command key 'getclusterparameter' is rejected as unknown command.""" + result = execute_admin_command(collection, {"getclusterparameter": "*"}) + assertFailureCode( + result, COMMAND_NOT_FOUND_ERROR, msg="Wrong-case command key should be rejected" + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py new file mode 100644 index 000000000..feada379e --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py @@ -0,0 +1,58 @@ +"""Tests for getClusterParameter authorization and namespace enforcement. + +Verifies admin-only restriction and access-control delegation: +non-admin databases are rejected with Unauthorized, and the command +succeeds when auth is disabled (the default test environment). + +Categories: #3 (non-admin-db + unauthorized), #5, #7 (auth), #18 +""" + +import pytest + +from documentdb_tests.compatibility.tests.system.diagnostic.utils.diagnostic_test_case import ( + DiagnosticTestCase, +) +from documentdb_tests.framework.assertions import assertFailureCode, assertSuccessPartial +from documentdb_tests.framework.error_codes import UNAUTHORIZED_ERROR +from documentdb_tests.framework.executor import execute_admin_command, execute_command +from documentdb_tests.framework.parametrize import pytest_params + +pytestmark = pytest.mark.admin + + +# --------------------------------------------------------------------------- +# §3 / §5 / §7 Admin-database restriction +# --------------------------------------------------------------------------- + +# Non-admin databases reject getClusterParameter with Unauthorized (code 13). +# We run via execute_command() (which uses the fixture's own non-admin database) +# to target a non-admin context without bypassing the executor restriction. +_NON_ADMIN_DB_TESTS: list[DiagnosticTestCase] = [ + DiagnosticTestCase( + id="user_db", + command={"getClusterParameter": "*"}, + use_admin=False, + error_code=UNAUTHORIZED_ERROR, + msg="getClusterParameter on a non-admin user database should fail with Unauthorized", + ), +] + + +@pytest.mark.parametrize("test", pytest_params(_NON_ADMIN_DB_TESTS)) +def test_getClusterParameter_non_admin_db_rejected(collection, test): + """Test getClusterParameter fails with Unauthorized on non-admin databases.""" + result = execute_command(collection, test.command) + assertFailureCode(result, test.error_code, msg=test.msg) + + +# --------------------------------------------------------------------------- +# §5 / §7 Auth-disabled: admin succeeds (foundational delegation — one case) +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_admin_succeeds_without_auth(collection): + """Test getClusterParameter on admin database succeeds when auth is disabled.""" + result = execute_admin_command(collection, {"getClusterParameter": "*"}) + assertSuccessPartial( + result, {"ok": 1.0}, msg="getClusterParameter on admin should succeed without auth" + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py new file mode 100644 index 000000000..5d5ae95d3 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py @@ -0,0 +1,111 @@ +"""Tests for getClusterParameter core retrieval behavior. + +Covers the three accepted argument forms, default values on a fresh +deployment, idempotency of repeated calls, and that requesting one name +does not return unrelated parameters. + +Categories: #4 (core behavior + deployment variants), #7, #14 +""" + +import pytest + +from documentdb_tests.framework.assertions import assertProperties, assertSuccessPartial +from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.property_checks import Eq, Len + +pytestmark = pytest.mark.admin + +_VALID_PARAM = "changeStreamOptions" +_VALID_PARAM_2 = "changeStreams" + + +# --------------------------------------------------------------------------- +# §4 / §7 Core behavior — three argument forms +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_single_name_succeeds(collection): + """Test single valid name returns ok:1 with clusterParameters length 1.""" + result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) + assertProperties( + result, + {"ok": Eq(1.0), "clusterParameters": Len(1)}, + msg="Single valid name should return ok:1 with one parameter", + raw_res=True, + ) + + +def test_getClusterParameter_wildcard_returns_all_params(collection): + """Test '*' returns ok:1.""" + result = execute_admin_command(collection, {"getClusterParameter": "*"}) + assertSuccessPartial(result, {"ok": 1.0}, msg="Wildcard should return ok:1") + + +def test_getClusterParameter_wildcard_includes_known_param(collection): + """Test '*' result includes the known parameter 'changeStreamOptions'.""" + result = execute_admin_command(collection, {"getClusterParameter": "*"}) + ids = [p["_id"] for p in result["clusterParameters"]] + assertProperties( + {"found": 1 if _VALID_PARAM in ids else 0}, + {"found": Eq(1)}, + msg=f"Wildcard result should include '{_VALID_PARAM}'", + raw_res=True, + ) + + +def test_getClusterParameter_array_two_names_succeeds(collection): + """Test array of two valid names returns ok:1 with clusterParameters length 2.""" + result = execute_admin_command( + collection, {"getClusterParameter": [_VALID_PARAM, _VALID_PARAM_2]} + ) + assertProperties( + result, + {"ok": Eq(1.0), "clusterParameters": Len(2)}, + msg="Array of two names should return ok:1 with two parameters", + raw_res=True, + ) + + +# --------------------------------------------------------------------------- +# §4 / §7 Defaults available on any deployment +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_wildcard_returns_defaults(collection): + """Test '*' returns defaults without error on any deployment.""" + result = execute_admin_command(collection, {"getClusterParameter": "*"}) + assertSuccessPartial(result, {"ok": 1.0}, msg="Defaults should be returned without prior set") + + +# --------------------------------------------------------------------------- +# §4 / §7 Requested name isolation +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_single_name_id_equals_request(collection): + """Test requesting one name returns element with _id equal to requested name.""" + result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) + assertProperties( + result, + {"clusterParameters.0._id": Eq(_VALID_PARAM)}, + msg=f"_id should equal the requested name '{_VALID_PARAM}'", + raw_res=True, + ) + + +# --------------------------------------------------------------------------- +# §14 Idempotency — repeated calls produce stable structure +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_wildcard_idempotent(collection): + """Test repeated '*' calls return the same parameter count.""" + r1 = execute_admin_command(collection, {"getClusterParameter": "*"}) + r2 = execute_admin_command(collection, {"getClusterParameter": "*"}) + count1 = len(r1["clusterParameters"]) + assertProperties( + {"count": len(r2["clusterParameters"])}, + {"count": Eq(count1)}, + msg="Repeated wildcard calls should return stable parameter count", + raw_res=True, + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py new file mode 100644 index 000000000..1185fb1ab --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py @@ -0,0 +1,149 @@ +"""Tests for getClusterParameter parameter name semantics. + +Verifies that parameter names are treated as exact literal strings: +no field-path traversal, no globbing, case-sensitive matching. +Also pins the unknown-name contract (error, not silent omission) and +verifies wildcard edge interactions in array form. + +Categories: #8, #9, #11 +""" + +from dataclasses import dataclass +from typing import Any + +import pytest + +from documentdb_tests.framework.assertions import assertFailureCode +from documentdb_tests.framework.error_codes import NO_SUCH_KEY_ERROR +from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_case import BaseTestCase + +pytestmark = pytest.mark.admin + +_VALID_PARAM = "changeStreamOptions" + + +# --------------------------------------------------------------------------- +# §8 Argument boundary probing — name strings are exact literals +# --------------------------------------------------------------------------- + + +@dataclass(frozen=True) +class NameCase(BaseTestCase): + """Test case for literal name semantics.""" + + name: Any = None + + +_BOUNDARY_NAMES: list[NameCase] = [ + NameCase( + "empty_string", + name="", + error_code=NO_SUCH_KEY_ERROR, + msg="Empty string should be treated as a literal no-match", + ), + NameCase( + "whitespace_only", + name=" ", + error_code=NO_SUCH_KEY_ERROR, + msg="Whitespace-only string should be treated as a literal no-match", + ), + NameCase( + "dotted_name", + name="a.b.c", + error_code=NO_SUCH_KEY_ERROR, + msg="Dotted name should not be traversed as a field path", + ), + NameCase( + "dollar_prefix", + name="$foo", + error_code=NO_SUCH_KEY_ERROR, + msg="Dollar-prefixed name should not be interpreted as an operator", + ), + NameCase( + "unicode_name", + name="paramétré", + error_code=NO_SUCH_KEY_ERROR, + msg="Unicode name should be treated as a literal no-match", + ), + NameCase( + "very_long_name", + name="x" * 10000, + error_code=NO_SUCH_KEY_ERROR, + msg="Very long name should not crash or timeout", + ), + NameCase( + "case_altered", + name="ChangeStreamOptions", + error_code=NO_SUCH_KEY_ERROR, + msg="Altered-case known name should not match (case-sensitive)", + ), +] + + +@pytest.mark.parametrize("test", pytest_params(_BOUNDARY_NAMES)) +def test_getClusterParameter_name_is_literal_string(collection, test): + """Test that parameter names are treated as exact literal strings.""" + result = execute_admin_command(collection, {"getClusterParameter": test.name}) + assertFailureCode(result, test.error_code, msg=test.msg) + + +# --------------------------------------------------------------------------- +# §9 Wildcard edge interactions +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_star_in_array_is_literal_name(collection): + """Test ['*'] treats '*' as a literal name (not expand-all) and errors.""" + result = execute_admin_command(collection, {"getClusterParameter": ["*"]}) + assertFailureCode( + result, NO_SUCH_KEY_ERROR, msg="'*' inside an array is a literal name, not a wildcard" + ) + + +def test_getClusterParameter_double_star_no_glob(collection): + """Test '**' is treated as a literal name, not a pattern.""" + result = execute_admin_command(collection, {"getClusterParameter": "**"}) + assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="'**' should not be treated as a glob") + + +def test_getClusterParameter_star_prefix_no_glob(collection): + """Test '*foo' is treated as a literal name, not a pattern.""" + result = execute_admin_command(collection, {"getClusterParameter": "*foo"}) + assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="'*foo' should not be treated as a glob") + + +def test_getClusterParameter_array_star_and_valid_name(collection): + """Test ['*', validName] treats '*' as a literal no-match and errors.""" + result = execute_admin_command(collection, {"getClusterParameter": ["*", _VALID_PARAM]}) + assertFailureCode( + result, + NO_SUCH_KEY_ERROR, + msg="'*' inside array with valid name should still error on '*' literal", + ) + + +# --------------------------------------------------------------------------- +# §11 Unknown-name contract consistency +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_unknown_single_string_errors(collection): + """Test single unknown string fails with NO_SUCH_KEY_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": "unknownParam"}) + assertFailureCode( + result, + NO_SUCH_KEY_ERROR, + msg="Single unknown name should fail with no-such-parameter error", + ) + + +def test_getClusterParameter_unknown_in_array_errors(collection): + """Test ['unknownParam'] fails with same error as 'unknownParam' string.""" + result = execute_admin_command(collection, {"getClusterParameter": ["unknownParam"]}) + assertFailureCode( + result, + NO_SUCH_KEY_ERROR, + msg="Array containing single unknown name should fail identically to string form", + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py new file mode 100644 index 000000000..0767cbc0a --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py @@ -0,0 +1,101 @@ +"""Tests for getClusterParameter response structure. + +Verifies the shape and BSON types of the success response: +clusterParameters is an array, each element has _id equal to the +requested name, ok is 1, and single-name requests are isolated. +Also validates value type fidelity for the changeStreamOptions parameter. + +Categories: #4 (response structure), #17 +""" + +import pytest + +from documentdb_tests.framework.assertions import assertProperties +from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.property_checks import Eq, IsType, Len + +pytestmark = pytest.mark.admin + +_VALID_PARAM = "changeStreamOptions" + + +# --------------------------------------------------------------------------- +# §4 Response shape +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_response_has_ok_1(collection): + """Test success response includes ok:1.""" + result = execute_admin_command(collection, {"getClusterParameter": "*"}) + assertProperties(result, {"ok": Eq(1.0)}, msg="Response ok should be 1.0", raw_res=True) + + +def test_getClusterParameter_response_clusterParameters_is_array(collection): + """Test success response contains 'clusterParameters' field of type array.""" + result = execute_admin_command(collection, {"getClusterParameter": "*"}) + assertProperties( + result, + {"clusterParameters": IsType("array")}, + msg="clusterParameters should be an array", + raw_res=True, + ) + + +def test_getClusterParameter_single_name_length_is_one(collection): + """Test single-name request returns exactly one element in clusterParameters.""" + result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) + assertProperties( + result, + {"clusterParameters": Len(1)}, + msg="Single-name request should return exactly one element", + raw_res=True, + ) + + +def test_getClusterParameter_element_has_id_field(collection): + """Test the clusterParameters element has a string '_id' field.""" + result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) + assertProperties( + result, + {"clusterParameters.0._id": IsType("string")}, + msg="_id field should be a string", + raw_res=True, + ) + + +def test_getClusterParameter_single_name_id_matches_request(collection): + """Test requesting one name returns element with _id equal to requested name.""" + result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) + assertProperties( + result, + {"clusterParameters.0._id": Eq(_VALID_PARAM)}, + msg=f"_id should equal requested name '{_VALID_PARAM}'", + raw_res=True, + ) + + +def test_getClusterParameter_element_has_nested_value_field(collection): + """Test document-valued parameter has a nested value sub-document.""" + result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) + assertProperties( + result, + {"clusterParameters.0.preAndPostImages": IsType("object")}, + msg="changeStreamOptions should contain 'preAndPostImages' sub-document", + raw_res=True, + ) + + +# --------------------------------------------------------------------------- +# §17 Value type fidelity +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_element_is_object_type(collection): + """Test the first clusterParameters element is a document (object type).""" + result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) + assertProperties( + result, + {"clusterParameters.0": IsType("object")}, + msg="clusterParameters element should be an object", + raw_res=True, + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py new file mode 100644 index 000000000..dbd523062 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py @@ -0,0 +1,92 @@ +"""Tests for getClusterParameter round-trip and differential behavior. + +Verifies read-after-write consistency with setClusterParameter, that +never-set parameters return their defaults, and that getClusterParameter +and getParameter operate on distinct namespaces. + +Categories: #12, #16 +""" + +import pytest +from bson import Int64 + +from documentdb_tests.framework.assertions import ( + assertFailureCode, + assertProperties, + assertSuccessPartial, +) +from documentdb_tests.framework.error_codes import INVALID_OPTIONS_ERROR +from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.property_checks import Eq, Len + +pytestmark = [pytest.mark.admin, pytest.mark.no_parallel] + +_PARAM = "changeStreamOptions" +_NESTED_KEY = "preAndPostImages" +_VALUE_KEY = "expireAfterSeconds" + + +def _get_expire_after_seconds(collection): + """Read the current expireAfterSeconds value from changeStreamOptions.""" + r = execute_admin_command(collection, {"getClusterParameter": _PARAM}) + assertSuccessPartial(r, {"ok": 1.0}, msg="getClusterParameter should succeed in setup") + return r["clusterParameters"][0][_NESTED_KEY][_VALUE_KEY] + + +def _set_expire_after_seconds(collection, value): + """Set expireAfterSeconds on changeStreamOptions.""" + execute_admin_command( + collection, + {"setClusterParameter": {_PARAM: {_NESTED_KEY: {_VALUE_KEY: value}}}}, + ) + + +# --------------------------------------------------------------------------- +# §12 Round-trip with setClusterParameter +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_reads_value_after_set(collection): + """Test getClusterParameter returns the value set by setClusterParameter.""" + original = _get_expire_after_seconds(collection) + # Server stores expireAfterSeconds as Int64; use same type for expected comparison. + new_value = Int64(7200) if int(original) != 7200 else Int64(3600) + try: + _set_expire_after_seconds(collection, int(new_value)) + result = execute_admin_command(collection, {"getClusterParameter": _PARAM}) + assertProperties( + result, + {"clusterParameters.0.preAndPostImages.expireAfterSeconds": Eq(new_value)}, + msg=f"expireAfterSeconds should equal {new_value} after set", + raw_res=True, + ) + finally: + _set_expire_after_seconds(collection, int(original)) + + +def test_getClusterParameter_never_set_returns_default(collection): + """Test a parameter that was never explicitly set returns a default without error.""" + result = execute_admin_command( + collection, {"getClusterParameter": "internalQueryCutoffForSampleFromRandomCursor"} + ) + assertProperties( + result, + {"ok": Eq(1.0), "clusterParameters": Len(1)}, + msg="Never-set parameter should return default without error", + raw_res=True, + ) + + +# --------------------------------------------------------------------------- +# §16 Differential: getClusterParameter vs getParameter have distinct namespaces +# --------------------------------------------------------------------------- + + +def test_getClusterParameter_name_not_retrievable_via_getParameter(collection): + """Test a cluster parameter name cannot be retrieved via getParameter.""" + result = execute_admin_command(collection, {"getParameter": _PARAM}) + assertFailureCode( + result, + INVALID_OPTIONS_ERROR, + msg="Cluster parameter name should not be retrievable via getParameter", + ) From 965a24d078652ad81fd2dca2d3c351ea4e689247 Mon Sep 17 00:00:00 2001 From: PatersonProjects Date: Fri, 26 Jun 2026 14:51:53 -0700 Subject: [PATCH 3/5] Moved test cases Signed-off-by: PatersonProjects --- ...t_getClusterParameter_argument_handling.py | 154 +--------- .../test_getClusterParameter_authorization.py | 44 --- .../test_getClusterParameter_core_behavior.py | 26 -- .../test_getClusterParameter_errors.py | 262 ++++++++++++++++++ ...test_getClusterParameter_name_semantics.py | 141 ---------- ..._getClusterParameter_response_structure.py | 10 - .../test_getClusterParameter_round_trip.py | 27 +- .../getClusterParameter/utils/__init__.py | 32 --- 8 files changed, 268 insertions(+), 428 deletions(-) delete mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py delete mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py delete mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/utils/__init__.py diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py index 825c06b05..81dcb7376 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py @@ -1,32 +1,16 @@ -"""Tests for getClusterParameter argument handling. +"""Tests for getClusterParameter accepted argument forms. -Covers BSON type rejection/acceptance for the argument, -argument forms (single/array/wildcard/empty), undocumented coercion -edge cases, array-form edge cases, and command-document quirks. +Covers the accepted argument forms: single string, wildcard, array of +strings, duplicate names, and the extra comment field. -Categories: #1, #2, #3 (type/field portions), #6, #10, #15 +Categories: #2, #10 """ import pytest -from documentdb_tests.framework.assertions import ( - assertFailureCode, - assertProperties, - assertSuccessPartial, -) -from documentdb_tests.framework.bson_type_validator import ( - BsonTypeTestCase, - generate_bson_rejection_test_cases, -) -from documentdb_tests.framework.error_codes import ( - BAD_VALUE_ERROR, - COMMAND_NOT_FOUND_ERROR, - NO_SUCH_KEY_ERROR, - TYPE_MISMATCH_ERROR, -) +from documentdb_tests.framework.assertions import assertProperties, assertSuccessPartial from documentdb_tests.framework.executor import execute_admin_command from documentdb_tests.framework.property_checks import Eq, Len -from documentdb_tests.framework.test_constants import BsonType pytestmark = pytest.mark.admin @@ -34,43 +18,6 @@ _VALID_PARAM_2 = "changeStreams" -# --------------------------------------------------------------------------- -# §1 / §6 BSON type rejection for non-string / non-array argument types -# The argument must be a string (single name or '*') or an array of strings. -# Every other BSON type is rejected with a type-mismatch error. NULL is handled -# separately in the coercion tests below. -# --------------------------------------------------------------------------- - -_ARGUMENT_TYPE_SPEC = [ - BsonTypeTestCase( - id="getClusterParameter_argument", - msg="getClusterParameter should reject non-string/non-array argument types", - keyword="getClusterParameter", - valid_types=[BsonType.STRING, BsonType.ARRAY], - default_error_code=TYPE_MISMATCH_ERROR, - skip_rejection_types=[BsonType.NULL], - ), -] - -_ARGUMENT_REJECTION_CASES = generate_bson_rejection_test_cases(_ARGUMENT_TYPE_SPEC) - - -@pytest.mark.parametrize("bson_type,sample_value,spec", _ARGUMENT_REJECTION_CASES) -def test_getClusterParameter_argument_rejects_type(collection, bson_type, sample_value, spec): - """Test getClusterParameter rejects non-string/non-array argument types.""" - result = execute_admin_command(collection, {"getClusterParameter": sample_value}) - assertFailureCode( - result, - spec.expected_code(bson_type), - msg=f"getClusterParameter should reject {bson_type.value} argument.", - ) - - -# --------------------------------------------------------------------------- -# §2 Argument forms — accepted -# --------------------------------------------------------------------------- - - def test_getClusterParameter_wildcard_returns_all(collection): """Test '*' argument returns all available cluster parameters with ok:1.""" result = execute_admin_command(collection, {"getClusterParameter": "*"}) @@ -109,98 +56,7 @@ def test_getClusterParameter_array_duplicate_names(collection): assertSuccessPartial(result, {"ok": 1.0}, msg="Duplicate names in array should succeed") -# --------------------------------------------------------------------------- -# §2 Argument forms — rejected -# --------------------------------------------------------------------------- - - -def test_getClusterParameter_empty_array_errors(collection): - """Test empty array argument fails with BAD_VALUE_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": []}) - assertFailureCode(result, BAD_VALUE_ERROR, msg="Empty array must supply at least one name") - - -def test_getClusterParameter_array_nonstring_element_rejects(collection): - """Test array containing a non-string element fails with TYPE_MISMATCH_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": [_VALID_PARAM, 123]}) - assertFailureCode( - result, TYPE_MISMATCH_ERROR, msg="Non-string array element should be rejected" - ) - - -def test_getClusterParameter_unknown_name_errors(collection): - """Test an unknown parameter name fails with NO_SUCH_KEY_ERROR (code 4).""" - result = execute_admin_command(collection, {"getClusterParameter": "doesNotExist"}) - assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="Unknown parameter name should fail") - - def test_getClusterParameter_unrecognized_field_accepted(collection): """Test extra comment field is accepted (MongoDB treats it as generic command field).""" result = execute_admin_command(collection, {"getClusterParameter": "*", "comment": "test"}) assertSuccessPartial(result, {"ok": 1.0}, msg="comment field should be accepted") - - -# --------------------------------------------------------------------------- -# §6 Undocumented coercion edge cases -# --------------------------------------------------------------------------- - - -def test_getClusterParameter_null_argument(collection): - """Test a null argument is rejected with a type-mismatch error.""" - result = execute_admin_command(collection, {"getClusterParameter": None}) - assertFailureCode( - result, - TYPE_MISMATCH_ERROR, - msg="Null argument should be rejected as a type mismatch.", - ) - - -def test_getClusterParameter_empty_string_argument(collection): - """Test an empty-string argument is treated as an unknown parameter name.""" - result = execute_admin_command(collection, {"getClusterParameter": ""}) - assertFailureCode( - result, - NO_SUCH_KEY_ERROR, - msg="Empty-string argument should be treated as an unknown parameter name.", - ) - - -def test_getClusterParameter_array_null_element_rejects(collection): - """Test array containing null element fails with TYPE_MISMATCH_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": [None]}) - assertFailureCode(result, TYPE_MISMATCH_ERROR, msg="Null element in array should be rejected") - - -def test_getClusterParameter_array_doc_element_rejects(collection): - """Test array containing a document element fails with TYPE_MISMATCH_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": [{"a": 1}]}) - assertFailureCode( - result, TYPE_MISMATCH_ERROR, msg="Document element in array should be rejected" - ) - - -def test_getClusterParameter_array_nested_array_rejects(collection): - """Test array containing a nested array element fails with TYPE_MISMATCH_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": [[_VALID_PARAM]]}) - assertFailureCode(result, TYPE_MISMATCH_ERROR, msg="Nested array element should be rejected") - - -def test_getClusterParameter_array_mixed_valid_unknown_errors(collection): - """Test array with one valid and one unknown name fails with NO_SUCH_KEY_ERROR.""" - result = execute_admin_command( - collection, {"getClusterParameter": [_VALID_PARAM, "unknownParam"]} - ) - assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="Unknown entry in mixed array should fail") - - -# --------------------------------------------------------------------------- -# §15 Command-document quirks -# --------------------------------------------------------------------------- - - -def test_getClusterParameter_wrong_case_command_key_rejected(collection): - """Test wrong-case command key 'getclusterparameter' is rejected as unknown command.""" - result = execute_admin_command(collection, {"getclusterparameter": "*"}) - assertFailureCode( - result, COMMAND_NOT_FOUND_ERROR, msg="Wrong-case command key should be rejected" - ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py deleted file mode 100644 index 2be687b59..000000000 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_authorization.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Tests for getClusterParameter authorization and namespace enforcement. - -Verifies admin-only restriction and access-control delegation: -non-admin databases are rejected with Unauthorized, and the command -succeeds when auth is disabled (the default test environment). - -Categories: #3 (non-admin-db + unauthorized), #5, #7 (auth), #18 -""" - -import pytest - -from documentdb_tests.framework.assertions import assertFailureCode, assertSuccessPartial -from documentdb_tests.framework.error_codes import UNAUTHORIZED_ERROR -from documentdb_tests.framework.executor import execute_admin_command, execute_command - -pytestmark = [pytest.mark.admin, pytest.mark.rbac] - - -# --------------------------------------------------------------------------- -# §3 / §5 / §7 Admin-database restriction -# --------------------------------------------------------------------------- - - -def test_getClusterParameter_rejected_on_non_admin_database(collection): - """Test getClusterParameter is rejected against a non-admin database.""" - result = execute_command(collection, {"getClusterParameter": "*"}) - assertFailureCode( - result, - UNAUTHORIZED_ERROR, - msg="getClusterParameter should be rejected on a non-admin database.", - ) - - -# --------------------------------------------------------------------------- -# §5 / §7 Auth-disabled: admin succeeds (foundational delegation — one case) -# --------------------------------------------------------------------------- - - -def test_getClusterParameter_admin_succeeds_without_auth(collection): - """Test getClusterParameter on admin database succeeds when auth is disabled.""" - result = execute_admin_command(collection, {"getClusterParameter": "*"}) - assertSuccessPartial( - result, {"ok": 1.0}, msg="getClusterParameter on admin should succeed without auth" - ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py index 5d5ae95d3..246fe7a40 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py @@ -19,11 +19,6 @@ _VALID_PARAM_2 = "changeStreams" -# --------------------------------------------------------------------------- -# §4 / §7 Core behavior — three argument forms -# --------------------------------------------------------------------------- - - def test_getClusterParameter_single_name_succeeds(collection): """Test single valid name returns ok:1 with clusterParameters length 1.""" result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) @@ -66,22 +61,6 @@ def test_getClusterParameter_array_two_names_succeeds(collection): ) -# --------------------------------------------------------------------------- -# §4 / §7 Defaults available on any deployment -# --------------------------------------------------------------------------- - - -def test_getClusterParameter_wildcard_returns_defaults(collection): - """Test '*' returns defaults without error on any deployment.""" - result = execute_admin_command(collection, {"getClusterParameter": "*"}) - assertSuccessPartial(result, {"ok": 1.0}, msg="Defaults should be returned without prior set") - - -# --------------------------------------------------------------------------- -# §4 / §7 Requested name isolation -# --------------------------------------------------------------------------- - - def test_getClusterParameter_single_name_id_equals_request(collection): """Test requesting one name returns element with _id equal to requested name.""" result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) @@ -93,11 +72,6 @@ def test_getClusterParameter_single_name_id_equals_request(collection): ) -# --------------------------------------------------------------------------- -# §14 Idempotency — repeated calls produce stable structure -# --------------------------------------------------------------------------- - - def test_getClusterParameter_wildcard_idempotent(collection): """Test repeated '*' calls return the same parameter count.""" r1 = execute_admin_command(collection, {"getClusterParameter": "*"}) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py new file mode 100644 index 000000000..4efa14ecc --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py @@ -0,0 +1,262 @@ +"""Tests for getClusterParameter error cases. + +Consolidates all error-producing inputs: BSON type rejection for the +argument field, unknown parameter names, invalid argument forms, null +coercion, array element type errors, namespace enforcement, and +command-key case sensitivity. + +Categories: #1, #2, #3, #6, #11, #15, #16, #18 +""" + +from dataclasses import dataclass +from typing import Any + +import pytest + +from documentdb_tests.framework.assertions import assertFailureCode +from documentdb_tests.framework.bson_type_validator import ( + BsonTypeTestCase, + generate_bson_rejection_test_cases, +) +from documentdb_tests.framework.error_codes import ( + BAD_VALUE_ERROR, + COMMAND_NOT_FOUND_ERROR, + INVALID_OPTIONS_ERROR, + NO_SUCH_KEY_ERROR, + TYPE_MISMATCH_ERROR, + UNAUTHORIZED_ERROR, +) +from documentdb_tests.framework.executor import execute_admin_command, execute_command +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_case import BaseTestCase +from documentdb_tests.framework.test_constants import BsonType + +pytestmark = pytest.mark.admin + +_VALID_PARAM = "changeStreamOptions" +_PARAM = "changeStreamOptions" + + +_ARGUMENT_TYPE_SPEC = [ + BsonTypeTestCase( + id="getClusterParameter_argument", + msg="getClusterParameter should reject non-string/non-array argument types", + keyword="getClusterParameter", + valid_types=[BsonType.STRING, BsonType.ARRAY], + default_error_code=TYPE_MISMATCH_ERROR, + skip_rejection_types=[BsonType.NULL], + ), +] + +_ARGUMENT_REJECTION_CASES = generate_bson_rejection_test_cases(_ARGUMENT_TYPE_SPEC) + + +@pytest.mark.parametrize("bson_type,sample_value,spec", _ARGUMENT_REJECTION_CASES) +def test_getClusterParameter_argument_rejects_type(collection, bson_type, sample_value, spec): + """Test getClusterParameter rejects non-string/non-array argument types.""" + result = execute_admin_command(collection, {"getClusterParameter": sample_value}) + assertFailureCode( + result, + spec.expected_code(bson_type), + msg=f"getClusterParameter should reject {bson_type.value} argument.", + ) + + +@dataclass(frozen=True) +class NameCase(BaseTestCase): + """Test case for literal name semantics.""" + + name: Any = None + + +_BOUNDARY_NAMES: list[NameCase] = [ + NameCase( + "whitespace_only", + name=" ", + error_code=NO_SUCH_KEY_ERROR, + msg="Whitespace-only string should be treated as a literal no-match", + ), + NameCase( + "dotted_name", + name="a.b.c", + error_code=NO_SUCH_KEY_ERROR, + msg="Dotted name should not be traversed as a field path", + ), + NameCase( + "dollar_prefix", + name="$foo", + error_code=NO_SUCH_KEY_ERROR, + msg="Dollar-prefixed name should not be interpreted as an operator", + ), + NameCase( + "unicode_name", + name="paramétré", + error_code=NO_SUCH_KEY_ERROR, + msg="Unicode name should be treated as a literal no-match", + ), + NameCase( + "very_long_name", + name="x" * 10000, + error_code=NO_SUCH_KEY_ERROR, + msg="Very long name should not crash or timeout", + ), + NameCase( + "case_altered", + name="ChangeStreamOptions", + error_code=NO_SUCH_KEY_ERROR, + msg="Altered-case known name should not match (case-sensitive)", + ), +] + + +@pytest.mark.parametrize("test", pytest_params(_BOUNDARY_NAMES)) +def test_getClusterParameter_name_is_literal_string(collection, test): + """Test that parameter names are treated as exact literal strings.""" + result = execute_admin_command(collection, {"getClusterParameter": test.name}) + assertFailureCode(result, test.error_code, msg=test.msg) + + +def test_getClusterParameter_star_in_array_is_literal_name(collection): + """Test ['*'] treats '*' as a literal name (not expand-all) and errors.""" + result = execute_admin_command(collection, {"getClusterParameter": ["*"]}) + assertFailureCode( + result, NO_SUCH_KEY_ERROR, msg="'*' inside an array is a literal name, not a wildcard" + ) + + +def test_getClusterParameter_double_star_no_glob(collection): + """Test '**' is treated as a literal name, not a pattern.""" + result = execute_admin_command(collection, {"getClusterParameter": "**"}) + assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="'**' should not be treated as a glob") + + +def test_getClusterParameter_star_prefix_no_glob(collection): + """Test '*foo' is treated as a literal name, not a pattern.""" + result = execute_admin_command(collection, {"getClusterParameter": "*foo"}) + assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="'*foo' should not be treated as a glob") + + +def test_getClusterParameter_array_star_and_valid_name(collection): + """Test ['*', validName] treats '*' as a literal no-match and errors.""" + result = execute_admin_command(collection, {"getClusterParameter": ["*", _VALID_PARAM]}) + assertFailureCode( + result, + NO_SUCH_KEY_ERROR, + msg="'*' inside array with valid name should still error on '*' literal", + ) + + +def test_getClusterParameter_unknown_single_string_errors(collection): + """Test single unknown string fails with NO_SUCH_KEY_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": "unknownParam"}) + assertFailureCode( + result, + NO_SUCH_KEY_ERROR, + msg="Single unknown name should fail with no-such-parameter error", + ) + + +def test_getClusterParameter_unknown_in_array_errors(collection): + """Test ['unknownParam'] fails with same error as 'unknownParam' string.""" + result = execute_admin_command(collection, {"getClusterParameter": ["unknownParam"]}) + assertFailureCode( + result, + NO_SUCH_KEY_ERROR, + msg="Array containing single unknown name should fail identically to string form", + ) + + +def test_getClusterParameter_empty_array_errors(collection): + """Test empty array argument fails with BAD_VALUE_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": []}) + assertFailureCode(result, BAD_VALUE_ERROR, msg="Empty array must supply at least one name") + + +def test_getClusterParameter_array_nonstring_element_rejects(collection): + """Test array containing a non-string element fails with TYPE_MISMATCH_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": [_VALID_PARAM, 123]}) + assertFailureCode( + result, TYPE_MISMATCH_ERROR, msg="Non-string array element should be rejected" + ) + + +def test_getClusterParameter_unknown_name_errors(collection): + """Test an unknown parameter name fails with NO_SUCH_KEY_ERROR (code 4).""" + result = execute_admin_command(collection, {"getClusterParameter": "doesNotExist"}) + assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="Unknown parameter name should fail") + + +def test_getClusterParameter_null_argument(collection): + """Test a null argument is rejected with a type-mismatch error.""" + result = execute_admin_command(collection, {"getClusterParameter": None}) + assertFailureCode( + result, + TYPE_MISMATCH_ERROR, + msg="Null argument should be rejected as a type mismatch.", + ) + + +def test_getClusterParameter_empty_string_argument(collection): + """Test an empty-string argument is treated as an unknown parameter name.""" + result = execute_admin_command(collection, {"getClusterParameter": ""}) + assertFailureCode( + result, + NO_SUCH_KEY_ERROR, + msg="Empty-string argument should be treated as an unknown parameter name.", + ) + + +def test_getClusterParameter_array_null_element_rejects(collection): + """Test array containing null element fails with TYPE_MISMATCH_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": [None]}) + assertFailureCode(result, TYPE_MISMATCH_ERROR, msg="Null element in array should be rejected") + + +def test_getClusterParameter_array_doc_element_rejects(collection): + """Test array containing a document element fails with TYPE_MISMATCH_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": [{"a": 1}]}) + assertFailureCode( + result, TYPE_MISMATCH_ERROR, msg="Document element in array should be rejected" + ) + + +def test_getClusterParameter_array_nested_array_rejects(collection): + """Test array containing a nested array element fails with TYPE_MISMATCH_ERROR.""" + result = execute_admin_command(collection, {"getClusterParameter": [[_VALID_PARAM]]}) + assertFailureCode(result, TYPE_MISMATCH_ERROR, msg="Nested array element should be rejected") + + +def test_getClusterParameter_array_mixed_valid_unknown_errors(collection): + """Test array with one valid and one unknown name fails with NO_SUCH_KEY_ERROR.""" + result = execute_admin_command( + collection, {"getClusterParameter": [_VALID_PARAM, "unknownParam"]} + ) + assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="Unknown entry in mixed array should fail") + + +def test_getClusterParameter_wrong_case_command_key_rejected(collection): + """Test wrong-case command key 'getclusterparameter' is rejected as unknown command.""" + result = execute_admin_command(collection, {"getclusterparameter": "*"}) + assertFailureCode( + result, COMMAND_NOT_FOUND_ERROR, msg="Wrong-case command key should be rejected" + ) + + +def test_getClusterParameter_rejected_on_non_admin_database(collection): + """Test getClusterParameter is rejected against a non-admin database.""" + result = execute_command(collection, {"getClusterParameter": "*"}) + assertFailureCode( + result, + UNAUTHORIZED_ERROR, + msg="getClusterParameter should be rejected on a non-admin database.", + ) + + +def test_getClusterParameter_name_not_retrievable_via_getParameter(collection): + """Test a cluster parameter name cannot be retrieved via getParameter.""" + result = execute_admin_command(collection, {"getParameter": 1, _PARAM: 1}) + assertFailureCode( + result, + INVALID_OPTIONS_ERROR, + msg="Cluster parameter name should not be retrievable via getParameter", + ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py deleted file mode 100644 index 8c695519f..000000000 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_name_semantics.py +++ /dev/null @@ -1,141 +0,0 @@ -"""Tests for how getClusterParameter interprets parameter names. - -Parameter names are exact-match literals: there is no field-path traversal, -operator interpretation, globbing, or case folding. Any name that is not an -exact match for an existing cluster parameter is reported as unknown, and the -single-name and array forms report an unknown name identically. -""" - -from dataclasses import dataclass -from typing import Any - -import pytest - -from documentdb_tests.framework.assertions import assertFailureCode -from documentdb_tests.framework.error_codes import NO_SUCH_KEY_ERROR -from documentdb_tests.framework.executor import execute_admin_command -from documentdb_tests.framework.parametrize import pytest_params -from documentdb_tests.framework.test_case import BaseTestCase - -pytestmark = pytest.mark.admin - -_VALID_PARAM = "changeStreamOptions" - - -# --------------------------------------------------------------------------- -# §8 Argument boundary probing — name strings are exact literals -# --------------------------------------------------------------------------- - - -@dataclass(frozen=True) -class NameCase(BaseTestCase): - """Test case for literal name semantics.""" - - name: Any = None - - -_BOUNDARY_NAMES: list[NameCase] = [ - NameCase( - "whitespace_only", - name=" ", - error_code=NO_SUCH_KEY_ERROR, - msg="Whitespace-only string should be treated as a literal no-match", - ), - NameCase( - "dotted_name", - name="a.b.c", - error_code=NO_SUCH_KEY_ERROR, - msg="Dotted name should not be traversed as a field path", - ), - NameCase( - "dollar_prefix", - name="$foo", - error_code=NO_SUCH_KEY_ERROR, - msg="Dollar-prefixed name should not be interpreted as an operator", - ), - NameCase( - "unicode_name", - name="paramétré", - error_code=NO_SUCH_KEY_ERROR, - msg="Unicode name should be treated as a literal no-match", - ), - NameCase( - "very_long_name", - name="x" * 10000, - error_code=NO_SUCH_KEY_ERROR, - msg="Very long name should not crash or timeout", - ), - NameCase( - "case_altered", - name="ChangeStreamOptions", - error_code=NO_SUCH_KEY_ERROR, - msg="Altered-case known name should not match (case-sensitive)", - ), -] - - -@pytest.mark.parametrize("test", pytest_params(_BOUNDARY_NAMES)) -def test_getClusterParameter_name_is_literal_string(collection, test): - """Test that parameter names are treated as exact literal strings.""" - result = execute_admin_command(collection, {"getClusterParameter": test.name}) - assertFailureCode(result, test.error_code, msg=test.msg) - - -# --------------------------------------------------------------------------- -# §9 Wildcard edge interactions -# --------------------------------------------------------------------------- - - -def test_getClusterParameter_star_in_array_is_literal_name(collection): - """Test ['*'] treats '*' as a literal name (not expand-all) and errors.""" - result = execute_admin_command(collection, {"getClusterParameter": ["*"]}) - assertFailureCode( - result, NO_SUCH_KEY_ERROR, msg="'*' inside an array is a literal name, not a wildcard" - ) - - -def test_getClusterParameter_double_star_no_glob(collection): - """Test '**' is treated as a literal name, not a pattern.""" - result = execute_admin_command(collection, {"getClusterParameter": "**"}) - assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="'**' should not be treated as a glob") - - -def test_getClusterParameter_star_prefix_no_glob(collection): - """Test '*foo' is treated as a literal name, not a pattern.""" - result = execute_admin_command(collection, {"getClusterParameter": "*foo"}) - assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="'*foo' should not be treated as a glob") - - -def test_getClusterParameter_array_star_and_valid_name(collection): - """Test ['*', validName] treats '*' as a literal no-match and errors.""" - result = execute_admin_command(collection, {"getClusterParameter": ["*", _VALID_PARAM]}) - assertFailureCode( - result, - NO_SUCH_KEY_ERROR, - msg="'*' inside array with valid name should still error on '*' literal", - ) - - -# --------------------------------------------------------------------------- -# §11 Unknown-name contract consistency -# --------------------------------------------------------------------------- - - -def test_getClusterParameter_unknown_single_string_errors(collection): - """Test single unknown string fails with NO_SUCH_KEY_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": "unknownParam"}) - assertFailureCode( - result, - NO_SUCH_KEY_ERROR, - msg="Single unknown name should fail with no-such-parameter error", - ) - - -def test_getClusterParameter_unknown_in_array_errors(collection): - """Test ['unknownParam'] fails with same error as 'unknownParam' string.""" - result = execute_admin_command(collection, {"getClusterParameter": ["unknownParam"]}) - assertFailureCode( - result, - NO_SUCH_KEY_ERROR, - msg="Array containing single unknown name should fail identically to string form", - ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py index f111bc0f7..d0ffee130 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py @@ -24,11 +24,6 @@ _NUMERIC_TYPES = (int, float, Int64, Decimal128) -# --------------------------------------------------------------------------- -# §4 Response shape -# --------------------------------------------------------------------------- - - def test_getClusterParameter_response_has_ok_1(collection): """Test success response includes ok:1.""" result = execute_admin_command(collection, {"getClusterParameter": "*"}) @@ -101,11 +96,6 @@ def test_getClusterParameter_element_is_object_type(collection): ) -# --------------------------------------------------------------------------- -# §17 Value type fidelity -# --------------------------------------------------------------------------- - - def test_getClusterParameter_clusterParameterTime_is_timestamp_when_present(collection): """Test any clusterParameterTime field is a BSON Timestamp.""" params = execute_admin_command(collection, {"getClusterParameter": "*"})["clusterParameters"] diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py index edc2b294e..d64a3f8c4 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py @@ -10,12 +10,7 @@ import pytest from bson import Int64 -from documentdb_tests.framework.assertions import ( - assertFailureCode, - assertProperties, - assertSuccessPartial, -) -from documentdb_tests.framework.error_codes import INVALID_OPTIONS_ERROR +from documentdb_tests.framework.assertions import assertProperties, assertSuccessPartial from documentdb_tests.framework.executor import execute_admin_command from documentdb_tests.framework.property_checks import Eq, Len @@ -41,11 +36,6 @@ def _set_expire_after_seconds(collection, value): ) -# --------------------------------------------------------------------------- -# §12 Round-trip with setClusterParameter -# --------------------------------------------------------------------------- - - def test_getClusterParameter_reads_value_after_set(collection): """Test getClusterParameter returns the value set by setClusterParameter.""" original = _get_expire_after_seconds(collection) @@ -76,21 +66,6 @@ def test_getClusterParameter_never_set_returns_default(collection): ) -# --------------------------------------------------------------------------- -# §16 Differential: getClusterParameter vs getParameter have distinct namespaces -# --------------------------------------------------------------------------- - - -def test_getClusterParameter_name_not_retrievable_via_getParameter(collection): - """Test a cluster parameter name cannot be retrieved via getParameter.""" - result = execute_admin_command(collection, {"getParameter": 1, _PARAM: 1}) - assertFailureCode( - result, - INVALID_OPTIONS_ERROR, - msg="Cluster parameter name should not be retrievable via getParameter", - ) - - def test_getClusterParameter_server_parameter_not_a_cluster_parameter(collection): """Test a server parameter (logLevel) is not present in cluster parameters.""" names = [ diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/utils/__init__.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/utils/__init__.py deleted file mode 100644 index 940510b19..000000000 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/utils/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Shared helpers for getClusterParameter tests. - -Cluster parameter values and the exact set of available parameters vary by -deployment, so tests derive a valid parameter name at runtime from the -wildcard form rather than hard-coding one. -""" - -from documentdb_tests.framework.executor import execute_admin_command - - -def all_cluster_parameters(collection): - """Return the ``clusterParameters`` list from ``getClusterParameter: '*'``. - - Raises the underlying exception if the command did not succeed, so callers - fail loudly during setup rather than asserting against an error object. - """ - result = execute_admin_command(collection, {"getClusterParameter": "*"}) - if isinstance(result, Exception): - raise result - return result["clusterParameters"] - - -def valid_parameter_names(collection, count=1): - """Return up to ``count`` valid cluster parameter names for this deployment.""" - params = all_cluster_parameters(collection) - names = [p["_id"] for p in params] - if len(names) < count: - raise AssertionError( - f"deployment exposes only {len(names)} cluster parameter(s), " - f"need {count} for this test" - ) - return names[:count] From 9ae0d1794f21281e2aaebfea58aae1535b244f85 Mon Sep 17 00:00:00 2001 From: PatersonProjects Date: Mon, 29 Jun 2026 14:12:16 -0700 Subject: [PATCH 4/5] Parametrized test, removed duplicates and out of scope cases, added missing coverage Signed-off-by: PatersonProjects --- ...t_getClusterParameter_argument_handling.py | 122 ++++--- .../test_getClusterParameter_core_behavior.py | 96 ++---- .../test_getClusterParameter_errors.py | 302 +++++------------- ..._getClusterParameter_response_structure.py | 172 +++------- .../test_getClusterParameter_round_trip.py | 98 ------ .../system/administration/utils/__init__.py | 0 .../utils/administration_test_case.py | 50 +++ documentdb_tests/framework/property_checks.py | 19 ++ 8 files changed, 298 insertions(+), 561 deletions(-) delete mode 100644 documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/utils/__init__.py create mode 100644 documentdb_tests/compatibility/tests/system/administration/utils/administration_test_case.py diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py index 81dcb7376..5fdde8516 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py @@ -1,62 +1,92 @@ -"""Tests for getClusterParameter accepted argument forms. +"""Tests for getClusterParameter argument handling. -Covers the accepted argument forms: single string, wildcard, array of -strings, duplicate names, and the extra comment field. - -Categories: #2, #10 +Covers accepted argument forms (single string, wildcard, array of +strings, duplicate names, unrecognized extra field) and BSON type +rejection for the command argument. """ import pytest -from documentdb_tests.framework.assertions import assertProperties, assertSuccessPartial +from documentdb_tests.compatibility.tests.system.administration.utils.administration_test_case import ( # noqa: E501 + AdministrationTestCase, +) +from documentdb_tests.framework.assertions import assertFailureCode, assertProperties +from documentdb_tests.framework.bson_type_validator import ( + BsonTypeTestCase, + generate_bson_rejection_test_cases, +) +from documentdb_tests.framework.error_codes import TYPE_MISMATCH_ERROR from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.parametrize import pytest_params from documentdb_tests.framework.property_checks import Eq, Len +from documentdb_tests.framework.test_constants import BsonType pytestmark = pytest.mark.admin _VALID_PARAM = "changeStreamOptions" -_VALID_PARAM_2 = "changeStreams" - - -def test_getClusterParameter_wildcard_returns_all(collection): - """Test '*' argument returns all available cluster parameters with ok:1.""" - result = execute_admin_command(collection, {"getClusterParameter": "*"}) - assertSuccessPartial(result, {"ok": 1.0}, msg="Wildcard '*' should return ok:1") - - -def test_getClusterParameter_single_name_returns_one(collection): - """Test single string name returns exactly one clusterParameters entry.""" - result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) - assertProperties( +_VALID_PARAM_2 = "defaultMaxTimeMS" + +_ARGUMENT_TYPE_SPEC = [ + BsonTypeTestCase( + id="getClusterParameter_argument", + msg="getClusterParameter should reject non-string/non-array argument types", + keyword="getClusterParameter", + valid_types=[BsonType.STRING, BsonType.ARRAY], + default_error_code=TYPE_MISMATCH_ERROR, + skip_rejection_types=[BsonType.NULL], + ), +] + +_ARGUMENT_REJECTION_CASES = generate_bson_rejection_test_cases(_ARGUMENT_TYPE_SPEC) + + +@pytest.mark.parametrize("bson_type,sample_value,spec", _ARGUMENT_REJECTION_CASES) +def test_getClusterParameter_argument_rejects_type(collection, bson_type, sample_value, spec): + """Test getClusterParameter rejects non-string/non-array argument types.""" + result = execute_admin_command(collection, {"getClusterParameter": sample_value}) + assertFailureCode( result, - {"ok": Eq(1.0), "clusterParameters": Len(1)}, - msg="Single name should return ok:1 with one parameter", - raw_res=True, + spec.expected_code(bson_type), + msg=f"getClusterParameter should reject {bson_type.value} argument.", ) -def test_getClusterParameter_array_two_names_returns_two(collection): - """Test array of two valid names returns two clusterParameters entries.""" - result = execute_admin_command( - collection, {"getClusterParameter": [_VALID_PARAM, _VALID_PARAM_2]} - ) - assertProperties( - result, - {"ok": Eq(1.0), "clusterParameters": Len(2)}, +ARGUMENT_FORM_TESTS: list[AdministrationTestCase] = [ + AdministrationTestCase( + id="wildcard_returns_all", + command={"getClusterParameter": "*"}, + checks={"ok": Eq(1.0)}, + msg="Wildcard '*' should return ok:1", + ), + AdministrationTestCase( + id="single_name_returns_one", + command={"getClusterParameter": _VALID_PARAM}, + checks={"ok": Eq(1.0), "clusterParameters": Len(1)}, + msg="Single name should return ok:1 with one parameter", + ), + AdministrationTestCase( + id="array_two_names_returns_two", + command={"getClusterParameter": [_VALID_PARAM, _VALID_PARAM_2]}, + checks={"ok": Eq(1.0), "clusterParameters": Len(2)}, msg="Array of two names should return two parameters", - raw_res=True, - ) - - -def test_getClusterParameter_array_duplicate_names(collection): - """Test array with duplicate valid names succeeds.""" - result = execute_admin_command( - collection, {"getClusterParameter": [_VALID_PARAM, _VALID_PARAM]} - ) - assertSuccessPartial(result, {"ok": 1.0}, msg="Duplicate names in array should succeed") - - -def test_getClusterParameter_unrecognized_field_accepted(collection): - """Test extra comment field is accepted (MongoDB treats it as generic command field).""" - result = execute_admin_command(collection, {"getClusterParameter": "*", "comment": "test"}) - assertSuccessPartial(result, {"ok": 1.0}, msg="comment field should be accepted") + ), + AdministrationTestCase( + id="array_duplicate_names", + command={"getClusterParameter": [_VALID_PARAM, _VALID_PARAM]}, + checks={"ok": Eq(1.0)}, + msg="Duplicate names in array should succeed", + ), + AdministrationTestCase( + id="unrecognized_field_accepted", + command={"getClusterParameter": "*", "unknownField": "test"}, + checks={"ok": Eq(1.0)}, + msg="Unrecognized extra field should be accepted", + ), +] + + +@pytest.mark.parametrize("test", pytest_params(ARGUMENT_FORM_TESTS)) +def test_getClusterParameter_argument_forms(collection, test): + """Test accepted argument forms each return ok:1 with expected clusterParameters length.""" + result = execute_admin_command(collection, test.build_command()) + assertProperties(result, test.build_checks(), msg=test.msg, raw_res=True) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py index 246fe7a40..5e6cdaa96 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py @@ -1,85 +1,41 @@ """Tests for getClusterParameter core retrieval behavior. -Covers the three accepted argument forms, default values on a fresh -deployment, idempotency of repeated calls, and that requesting one name -does not return unrelated parameters. - -Categories: #4 (core behavior + deployment variants), #7, #14 +Verifies that the wildcard returns more than one cluster parameter and +that the result includes known parameters by name. """ import pytest -from documentdb_tests.framework.assertions import assertProperties, assertSuccessPartial +from documentdb_tests.compatibility.tests.system.administration.utils.administration_test_case import ( # noqa: E501 + AdministrationTestCase, +) +from documentdb_tests.framework.assertions import assertProperties from documentdb_tests.framework.executor import execute_admin_command -from documentdb_tests.framework.property_checks import Eq, Len +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.property_checks import Contains, LenGt pytestmark = pytest.mark.admin _VALID_PARAM = "changeStreamOptions" -_VALID_PARAM_2 = "changeStreams" - - -def test_getClusterParameter_single_name_succeeds(collection): - """Test single valid name returns ok:1 with clusterParameters length 1.""" - result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) - assertProperties( - result, - {"ok": Eq(1.0), "clusterParameters": Len(1)}, - msg="Single valid name should return ok:1 with one parameter", - raw_res=True, - ) - - -def test_getClusterParameter_wildcard_returns_all_params(collection): - """Test '*' returns ok:1.""" - result = execute_admin_command(collection, {"getClusterParameter": "*"}) - assertSuccessPartial(result, {"ok": 1.0}, msg="Wildcard should return ok:1") - -def test_getClusterParameter_wildcard_includes_known_param(collection): - """Test '*' result includes the known parameter 'changeStreamOptions'.""" - result = execute_admin_command(collection, {"getClusterParameter": "*"}) - ids = [p["_id"] for p in result["clusterParameters"]] - assertProperties( - {"found": 1 if _VALID_PARAM in ids else 0}, - {"found": Eq(1)}, +CORE_BEHAVIOR_TESTS: list[AdministrationTestCase] = [ + AdministrationTestCase( + id="wildcard_returns_multiple_params", + command={"getClusterParameter": "*"}, + checks={"clusterParameters": LenGt(1)}, + msg="Wildcard should return more than one cluster parameter", + ), + AdministrationTestCase( + id="wildcard_includes_known_param", + command={"getClusterParameter": "*"}, + checks={"clusterParameters": Contains("_id", _VALID_PARAM)}, msg=f"Wildcard result should include '{_VALID_PARAM}'", - raw_res=True, - ) - - -def test_getClusterParameter_array_two_names_succeeds(collection): - """Test array of two valid names returns ok:1 with clusterParameters length 2.""" - result = execute_admin_command( - collection, {"getClusterParameter": [_VALID_PARAM, _VALID_PARAM_2]} - ) - assertProperties( - result, - {"ok": Eq(1.0), "clusterParameters": Len(2)}, - msg="Array of two names should return ok:1 with two parameters", - raw_res=True, - ) - - -def test_getClusterParameter_single_name_id_equals_request(collection): - """Test requesting one name returns element with _id equal to requested name.""" - result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) - assertProperties( - result, - {"clusterParameters.0._id": Eq(_VALID_PARAM)}, - msg=f"_id should equal the requested name '{_VALID_PARAM}'", - raw_res=True, - ) + ), +] -def test_getClusterParameter_wildcard_idempotent(collection): - """Test repeated '*' calls return the same parameter count.""" - r1 = execute_admin_command(collection, {"getClusterParameter": "*"}) - r2 = execute_admin_command(collection, {"getClusterParameter": "*"}) - count1 = len(r1["clusterParameters"]) - assertProperties( - {"count": len(r2["clusterParameters"])}, - {"count": Eq(count1)}, - msg="Repeated wildcard calls should return stable parameter count", - raw_res=True, - ) +@pytest.mark.parametrize("test", pytest_params(CORE_BEHAVIOR_TESTS)) +def test_getClusterParameter_core_behavior(collection, test): + """Test core retrieval behavior: wildcard parameter count and inclusion.""" + result = execute_admin_command(collection, test.build_command()) + assertProperties(result, test.build_checks(), msg=test.msg, raw_res=True) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py index 4efa14ecc..a478241a4 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py @@ -1,245 +1,129 @@ """Tests for getClusterParameter error cases. -Consolidates all error-producing inputs: BSON type rejection for the -argument field, unknown parameter names, invalid argument forms, null -coercion, array element type errors, namespace enforcement, and -command-key case sensitivity. - -Categories: #1, #2, #3, #6, #11, #15, #16, #18 +Covers error-producing inputs: unknown parameter names, empty and +null arguments, array element type errors, command-key case +sensitivity, and key ordering enforcement. Also verifies that the +command is rejected on non-admin databases. """ -from dataclasses import dataclass -from typing import Any - import pytest -from documentdb_tests.framework.assertions import assertFailureCode -from documentdb_tests.framework.bson_type_validator import ( - BsonTypeTestCase, - generate_bson_rejection_test_cases, +from documentdb_tests.compatibility.tests.system.administration.utils.administration_test_case import ( # noqa: E501 + AdministrationTestCase, ) +from documentdb_tests.framework.assertions import assertFailureCode from documentdb_tests.framework.error_codes import ( BAD_VALUE_ERROR, COMMAND_NOT_FOUND_ERROR, - INVALID_OPTIONS_ERROR, NO_SUCH_KEY_ERROR, TYPE_MISMATCH_ERROR, UNAUTHORIZED_ERROR, ) from documentdb_tests.framework.executor import execute_admin_command, execute_command from documentdb_tests.framework.parametrize import pytest_params -from documentdb_tests.framework.test_case import BaseTestCase -from documentdb_tests.framework.test_constants import BsonType pytestmark = pytest.mark.admin _VALID_PARAM = "changeStreamOptions" -_PARAM = "changeStreamOptions" - - -_ARGUMENT_TYPE_SPEC = [ - BsonTypeTestCase( - id="getClusterParameter_argument", - msg="getClusterParameter should reject non-string/non-array argument types", - keyword="getClusterParameter", - valid_types=[BsonType.STRING, BsonType.ARRAY], - default_error_code=TYPE_MISMATCH_ERROR, - skip_rejection_types=[BsonType.NULL], - ), -] - -_ARGUMENT_REJECTION_CASES = generate_bson_rejection_test_cases(_ARGUMENT_TYPE_SPEC) - - -@pytest.mark.parametrize("bson_type,sample_value,spec", _ARGUMENT_REJECTION_CASES) -def test_getClusterParameter_argument_rejects_type(collection, bson_type, sample_value, spec): - """Test getClusterParameter rejects non-string/non-array argument types.""" - result = execute_admin_command(collection, {"getClusterParameter": sample_value}) - assertFailureCode( - result, - spec.expected_code(bson_type), - msg=f"getClusterParameter should reject {bson_type.value} argument.", - ) - -@dataclass(frozen=True) -class NameCase(BaseTestCase): - """Test case for literal name semantics.""" - name: Any = None - - -_BOUNDARY_NAMES: list[NameCase] = [ - NameCase( - "whitespace_only", - name=" ", - error_code=NO_SUCH_KEY_ERROR, - msg="Whitespace-only string should be treated as a literal no-match", - ), - NameCase( - "dotted_name", - name="a.b.c", +_NO_SUCH_KEY_CASES: list[AdministrationTestCase] = [ + AdministrationTestCase( + id="unknown_single_string_errors", + command={"getClusterParameter": "unknownParam"}, error_code=NO_SUCH_KEY_ERROR, - msg="Dotted name should not be traversed as a field path", + msg="Single unknown name should fail with no-such-parameter error", ), - NameCase( - "dollar_prefix", - name="$foo", + AdministrationTestCase( + id="empty_string_argument", + command={"getClusterParameter": ""}, error_code=NO_SUCH_KEY_ERROR, - msg="Dollar-prefixed name should not be interpreted as an operator", + msg="Empty-string argument should be treated as an unknown parameter name", ), - NameCase( - "unicode_name", - name="paramétré", + AdministrationTestCase( + id="case_altered", + command={"getClusterParameter": "ChangeStreamOptions"}, error_code=NO_SUCH_KEY_ERROR, - msg="Unicode name should be treated as a literal no-match", + msg="Altered-case known name should not match (case-sensitive)", ), - NameCase( - "very_long_name", - name="x" * 10000, + AdministrationTestCase( + id="star_in_array_is_literal_name", + command={"getClusterParameter": ["*"]}, error_code=NO_SUCH_KEY_ERROR, - msg="Very long name should not crash or timeout", + msg="'*' inside an array is a literal name, not a wildcard", ), - NameCase( - "case_altered", - name="ChangeStreamOptions", + AdministrationTestCase( + id="array_mixed_valid_unknown_errors", + command={"getClusterParameter": [_VALID_PARAM, "unknownParam"]}, error_code=NO_SUCH_KEY_ERROR, - msg="Altered-case known name should not match (case-sensitive)", + msg="Unknown entry in mixed array should fail", ), ] +_TYPE_ERROR_CASES: list[AdministrationTestCase] = [ + AdministrationTestCase( + id="empty_array_errors", + command={"getClusterParameter": []}, + error_code=BAD_VALUE_ERROR, + msg="Empty array must supply at least one name", + ), + AdministrationTestCase( + id="array_nonstring_element_rejects", + command={"getClusterParameter": [_VALID_PARAM, 123]}, + error_code=TYPE_MISMATCH_ERROR, + msg="Non-string array element should be rejected", + ), + AdministrationTestCase( + id="null_argument", + command={"getClusterParameter": None}, + error_code=TYPE_MISMATCH_ERROR, + msg="Null argument should be rejected as a type mismatch", + ), + AdministrationTestCase( + id="array_null_element_rejects", + command={"getClusterParameter": [None]}, + error_code=TYPE_MISMATCH_ERROR, + msg="Null element in array should be rejected", + ), + AdministrationTestCase( + id="array_doc_element_rejects", + command={"getClusterParameter": [{"a": 1}]}, + error_code=TYPE_MISMATCH_ERROR, + msg="Document element in array should be rejected", + ), + AdministrationTestCase( + id="array_nested_array_rejects", + command={"getClusterParameter": [[_VALID_PARAM]]}, + error_code=TYPE_MISMATCH_ERROR, + msg="Nested array element should be rejected", + ), +] -@pytest.mark.parametrize("test", pytest_params(_BOUNDARY_NAMES)) -def test_getClusterParameter_name_is_literal_string(collection, test): - """Test that parameter names are treated as exact literal strings.""" - result = execute_admin_command(collection, {"getClusterParameter": test.name}) - assertFailureCode(result, test.error_code, msg=test.msg) - - -def test_getClusterParameter_star_in_array_is_literal_name(collection): - """Test ['*'] treats '*' as a literal name (not expand-all) and errors.""" - result = execute_admin_command(collection, {"getClusterParameter": ["*"]}) - assertFailureCode( - result, NO_SUCH_KEY_ERROR, msg="'*' inside an array is a literal name, not a wildcard" - ) - - -def test_getClusterParameter_double_star_no_glob(collection): - """Test '**' is treated as a literal name, not a pattern.""" - result = execute_admin_command(collection, {"getClusterParameter": "**"}) - assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="'**' should not be treated as a glob") - - -def test_getClusterParameter_star_prefix_no_glob(collection): - """Test '*foo' is treated as a literal name, not a pattern.""" - result = execute_admin_command(collection, {"getClusterParameter": "*foo"}) - assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="'*foo' should not be treated as a glob") - - -def test_getClusterParameter_array_star_and_valid_name(collection): - """Test ['*', validName] treats '*' as a literal no-match and errors.""" - result = execute_admin_command(collection, {"getClusterParameter": ["*", _VALID_PARAM]}) - assertFailureCode( - result, - NO_SUCH_KEY_ERROR, - msg="'*' inside array with valid name should still error on '*' literal", - ) - - -def test_getClusterParameter_unknown_single_string_errors(collection): - """Test single unknown string fails with NO_SUCH_KEY_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": "unknownParam"}) - assertFailureCode( - result, - NO_SUCH_KEY_ERROR, - msg="Single unknown name should fail with no-such-parameter error", - ) - - -def test_getClusterParameter_unknown_in_array_errors(collection): - """Test ['unknownParam'] fails with same error as 'unknownParam' string.""" - result = execute_admin_command(collection, {"getClusterParameter": ["unknownParam"]}) - assertFailureCode( - result, - NO_SUCH_KEY_ERROR, - msg="Array containing single unknown name should fail identically to string form", - ) - - -def test_getClusterParameter_empty_array_errors(collection): - """Test empty array argument fails with BAD_VALUE_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": []}) - assertFailureCode(result, BAD_VALUE_ERROR, msg="Empty array must supply at least one name") - - -def test_getClusterParameter_array_nonstring_element_rejects(collection): - """Test array containing a non-string element fails with TYPE_MISMATCH_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": [_VALID_PARAM, 123]}) - assertFailureCode( - result, TYPE_MISMATCH_ERROR, msg="Non-string array element should be rejected" - ) - - -def test_getClusterParameter_unknown_name_errors(collection): - """Test an unknown parameter name fails with NO_SUCH_KEY_ERROR (code 4).""" - result = execute_admin_command(collection, {"getClusterParameter": "doesNotExist"}) - assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="Unknown parameter name should fail") - - -def test_getClusterParameter_null_argument(collection): - """Test a null argument is rejected with a type-mismatch error.""" - result = execute_admin_command(collection, {"getClusterParameter": None}) - assertFailureCode( - result, - TYPE_MISMATCH_ERROR, - msg="Null argument should be rejected as a type mismatch.", - ) - - -def test_getClusterParameter_empty_string_argument(collection): - """Test an empty-string argument is treated as an unknown parameter name.""" - result = execute_admin_command(collection, {"getClusterParameter": ""}) - assertFailureCode( - result, - NO_SUCH_KEY_ERROR, - msg="Empty-string argument should be treated as an unknown parameter name.", - ) - - -def test_getClusterParameter_array_null_element_rejects(collection): - """Test array containing null element fails with TYPE_MISMATCH_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": [None]}) - assertFailureCode(result, TYPE_MISMATCH_ERROR, msg="Null element in array should be rejected") - - -def test_getClusterParameter_array_doc_element_rejects(collection): - """Test array containing a document element fails with TYPE_MISMATCH_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": [{"a": 1}]}) - assertFailureCode( - result, TYPE_MISMATCH_ERROR, msg="Document element in array should be rejected" - ) - - -def test_getClusterParameter_array_nested_array_rejects(collection): - """Test array containing a nested array element fails with TYPE_MISMATCH_ERROR.""" - result = execute_admin_command(collection, {"getClusterParameter": [[_VALID_PARAM]]}) - assertFailureCode(result, TYPE_MISMATCH_ERROR, msg="Nested array element should be rejected") - +_COMMAND_ROUTING_CASES: list[AdministrationTestCase] = [ + AdministrationTestCase( + id="wrong_case_command_key_rejected", + command={"getclusterparameter": "*"}, + error_code=COMMAND_NOT_FOUND_ERROR, + msg="Wrong-case command key should be rejected", + ), + AdministrationTestCase( + id="command_key_not_first_fails", + command={"comment": "test", "getClusterParameter": "*"}, + error_code=COMMAND_NOT_FOUND_ERROR, + msg="getClusterParameter must be the first key in the command document", + ), +] -def test_getClusterParameter_array_mixed_valid_unknown_errors(collection): - """Test array with one valid and one unknown name fails with NO_SUCH_KEY_ERROR.""" - result = execute_admin_command( - collection, {"getClusterParameter": [_VALID_PARAM, "unknownParam"]} - ) - assertFailureCode(result, NO_SUCH_KEY_ERROR, msg="Unknown entry in mixed array should fail") +_ERROR_CASES: list[AdministrationTestCase] = ( + _NO_SUCH_KEY_CASES + _TYPE_ERROR_CASES + _COMMAND_ROUTING_CASES +) -def test_getClusterParameter_wrong_case_command_key_rejected(collection): - """Test wrong-case command key 'getclusterparameter' is rejected as unknown command.""" - result = execute_admin_command(collection, {"getclusterparameter": "*"}) - assertFailureCode( - result, COMMAND_NOT_FOUND_ERROR, msg="Wrong-case command key should be rejected" - ) +@pytest.mark.parametrize("test", pytest_params(_ERROR_CASES)) +def test_getClusterParameter_errors(collection, test): + """Test all error-producing inputs: unknown names, type mismatches, and command routing.""" + result = execute_admin_command(collection, test.build_command()) + assertFailureCode(result, test.error_code, msg=test.msg) def test_getClusterParameter_rejected_on_non_admin_database(collection): @@ -250,13 +134,3 @@ def test_getClusterParameter_rejected_on_non_admin_database(collection): UNAUTHORIZED_ERROR, msg="getClusterParameter should be rejected on a non-admin database.", ) - - -def test_getClusterParameter_name_not_retrievable_via_getParameter(collection): - """Test a cluster parameter name cannot be retrieved via getParameter.""" - result = execute_admin_command(collection, {"getParameter": 1, _PARAM: 1}) - assertFailureCode( - result, - INVALID_OPTIONS_ERROR, - msg="Cluster parameter name should not be retrievable via getParameter", - ) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py index d0ffee130..eced7ed4e 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py @@ -1,148 +1,54 @@ -"""Tests for getClusterParameter response structure and value-type fidelity. +"""Tests for getClusterParameter response structure. -Verifies the shape and BSON types of the success response: -clusterParameters is an array, each element has _id equal to the -requested name, ok is 1, and single-name requests are isolated. -Also validates BSON type fidelity (clusterParameterTime, numeric values). - -Categories: #4 (response structure), #17 +Verifies the top-level shape of the success response: ok is 1, +clusterParameters is an array, and a single-name request returns +exactly one element. """ import pytest -from bson import Decimal128, Int64, Timestamp +from documentdb_tests.compatibility.tests.system.administration.utils.administration_test_case import ( # noqa: E501 + AdministrationTestCase, +) from documentdb_tests.framework.assertions import assertProperties from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.parametrize import pytest_params from documentdb_tests.framework.property_checks import Eq, IsType, Len pytestmark = pytest.mark.admin _VALID_PARAM = "changeStreamOptions" -# Numeric BSON types that must survive a round trip without being coerced to -# string. bool is intentionally excluded (it is not a numeric value here). -_NUMERIC_TYPES = (int, float, Int64, Decimal128) - - -def test_getClusterParameter_response_has_ok_1(collection): - """Test success response includes ok:1.""" - result = execute_admin_command(collection, {"getClusterParameter": "*"}) - assertProperties(result, {"ok": Eq(1.0)}, msg="Response ok should be 1.0", raw_res=True) - - -def test_getClusterParameter_response_clusterParameters_is_array(collection): - """Test success response contains 'clusterParameters' field of type array.""" - result = execute_admin_command(collection, {"getClusterParameter": "*"}) - assertProperties( - result, - {"clusterParameters": IsType("array")}, +PROPERTY_TESTS: list[AdministrationTestCase] = [ + AdministrationTestCase( + id="ok_is_1", + command={"getClusterParameter": "*"}, + checks={"ok": Eq(1.0)}, + msg="Response ok should be 1.0", + ), + AdministrationTestCase( + id="clusterParameters_is_array", + command={"getClusterParameter": "*"}, + checks={"clusterParameters": IsType("array")}, msg="clusterParameters should be an array", - raw_res=True, - ) - - -def test_getClusterParameter_single_name_length_is_one(collection): - """Test single-name request returns exactly one element in clusterParameters.""" - result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) - assertProperties( - result, - {"clusterParameters": Len(1)}, + ), + AdministrationTestCase( + id="single_name_length_is_one", + command={"getClusterParameter": _VALID_PARAM}, + checks={"clusterParameters": Len(1)}, msg="Single-name request should return exactly one element", - raw_res=True, - ) - - -def test_getClusterParameter_element_has_id_field(collection): - """Test the clusterParameters element has a string '_id' field.""" - result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) - assertProperties( - result, - {"clusterParameters.0._id": IsType("string")}, - msg="_id field should be a string", - raw_res=True, - ) - - -def test_getClusterParameter_single_name_id_matches_request(collection): - """Test requesting one name returns element with _id equal to requested name.""" - result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) - assertProperties( - result, - {"clusterParameters.0._id": Eq(_VALID_PARAM)}, - msg=f"_id should equal requested name '{_VALID_PARAM}'", - raw_res=True, - ) - - -def test_getClusterParameter_element_has_nested_value_field(collection): - """Test document-valued parameter has a nested value sub-document.""" - result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) - assertProperties( - result, - {"clusterParameters.0.preAndPostImages": IsType("object")}, - msg="changeStreamOptions should contain 'preAndPostImages' sub-document", - raw_res=True, - ) - - -def test_getClusterParameter_element_is_object_type(collection): - """Test the first clusterParameters element is a document (object type).""" - result = execute_admin_command(collection, {"getClusterParameter": _VALID_PARAM}) - assertProperties( - result, - {"clusterParameters.0": IsType("object")}, - msg="clusterParameters element should be an object", - raw_res=True, - ) - - -def test_getClusterParameter_clusterParameterTime_is_timestamp_when_present(collection): - """Test any clusterParameterTime field is a BSON Timestamp.""" - params = execute_admin_command(collection, {"getClusterParameter": "*"})["clusterParameters"] - times = [e["clusterParameterTime"] for e in params if "clusterParameterTime" in e] - if not times: - pytest.skip("no parameter on this deployment carries clusterParameterTime") - all_timestamps = all(isinstance(t, Timestamp) for t in times) - assertProperties( - {"all_timestamps": all_timestamps}, - {"all_timestamps": Eq(True)}, - msg="clusterParameterTime should be a BSON Timestamp.", - raw_res=True, - ) - - -def test_getClusterParameter_numeric_value_not_coerced(collection): - """Test numeric parameter values keep a numeric BSON type (not stringified).""" - params = execute_admin_command(collection, {"getClusterParameter": "*"})["clusterParameters"] - - def first_numeric_type(value): - if isinstance(value, bool): - return None - if isinstance(value, _NUMERIC_TYPES): - return type(value).__name__ - if isinstance(value, dict): - for v in value.values(): - found = first_numeric_type(v) - if found: - return found - return None - - numeric_type = None - for entry in params: - for key, value in entry.items(): - if key == "_id": - continue - numeric_type = first_numeric_type(value) - if numeric_type: - break - if numeric_type: - break - if numeric_type is None: - pytest.skip("no numeric-valued cluster parameter on this deployment") - is_numeric_type = numeric_type in {"int", "float", "Int64", "Decimal128"} - assertProperties( - {"is_numeric_type": is_numeric_type}, - {"is_numeric_type": Eq(True)}, - msg=f"Numeric parameter value kept numeric BSON type '{numeric_type}' (not coerced).", - raw_res=True, - ) + ), + AdministrationTestCase( + id="element_id_matches_request", + command={"getClusterParameter": _VALID_PARAM}, + checks={"clusterParameters.0._id": Eq(_VALID_PARAM)}, + msg=f"Single-name request should return element with _id equal to '{_VALID_PARAM}'", + ), +] + + +@pytest.mark.parametrize("test", pytest_params(PROPERTY_TESTS)) +def test_getClusterParameter_response_properties(collection, test): + """Verifies getClusterParameter response fields have expected types and values.""" + result = execute_admin_command(collection, test.build_command()) + assertProperties(result, test.build_checks(), msg=test.msg, raw_res=True) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py deleted file mode 100644 index d64a3f8c4..000000000 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_round_trip.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Tests for getClusterParameter round-trip and differential behavior. - -Verifies read-after-write consistency with setClusterParameter, that -never-set parameters return their defaults, and that getClusterParameter -and getParameter operate on distinct namespaces. - -Categories: #12, #16 -""" - -import pytest -from bson import Int64 - -from documentdb_tests.framework.assertions import assertProperties, assertSuccessPartial -from documentdb_tests.framework.executor import execute_admin_command -from documentdb_tests.framework.property_checks import Eq, Len - -pytestmark = [pytest.mark.admin, pytest.mark.no_parallel] - -_PARAM = "changeStreamOptions" -_NESTED_KEY = "preAndPostImages" -_VALUE_KEY = "expireAfterSeconds" - - -def _get_expire_after_seconds(collection): - """Read the current expireAfterSeconds value from changeStreamOptions.""" - r = execute_admin_command(collection, {"getClusterParameter": _PARAM}) - assertSuccessPartial(r, {"ok": 1.0}, msg="getClusterParameter should succeed in setup") - return r["clusterParameters"][0][_NESTED_KEY][_VALUE_KEY] - - -def _set_expire_after_seconds(collection, value): - """Set expireAfterSeconds on changeStreamOptions.""" - execute_admin_command( - collection, - {"setClusterParameter": {_PARAM: {_NESTED_KEY: {_VALUE_KEY: value}}}}, - ) - - -def test_getClusterParameter_reads_value_after_set(collection): - """Test getClusterParameter returns the value set by setClusterParameter.""" - original = _get_expire_after_seconds(collection) - new_value = Int64(7200) if int(original) != 7200 else Int64(3600) - try: - _set_expire_after_seconds(collection, int(new_value)) - result = execute_admin_command(collection, {"getClusterParameter": _PARAM}) - assertProperties( - result, - {"clusterParameters.0.preAndPostImages.expireAfterSeconds": Eq(new_value)}, - msg=f"expireAfterSeconds should equal {new_value} after set", - raw_res=True, - ) - finally: - _set_expire_after_seconds(collection, int(original)) - - -def test_getClusterParameter_never_set_returns_default(collection): - """Test a parameter that was never explicitly set returns a default without error.""" - result = execute_admin_command( - collection, {"getClusterParameter": "internalQueryCutoffForSampleFromRandomCursor"} - ) - assertProperties( - result, - {"ok": Eq(1.0), "clusterParameters": Len(1)}, - msg="Never-set parameter should return default without error", - raw_res=True, - ) - - -def test_getClusterParameter_server_parameter_not_a_cluster_parameter(collection): - """Test a server parameter (logLevel) is not present in cluster parameters.""" - names = [ - p["_id"] - for p in execute_admin_command(collection, {"getClusterParameter": "*"})[ - "clusterParameters" - ] - ] - assertProperties( - {"has_logLevel": "logLevel" in names}, - {"has_logLevel": Eq(False)}, - msg="Server parameter 'logLevel' should not be a cluster parameter.", - raw_res=True, - ) - - -def test_getClusterParameter_defaultRWConcern_not_a_cluster_parameter(collection): - """Test defaultRWConcern is not a cluster parameter (distinct from getDefaultRWConcern).""" - names = [ - p["_id"] - for p in execute_admin_command(collection, {"getClusterParameter": "*"})[ - "clusterParameters" - ] - ] - assertProperties( - {"has_defaultRWConcern": "defaultRWConcern" in names}, - {"has_defaultRWConcern": Eq(False)}, - msg="defaultRWConcern should not appear among cluster parameters.", - raw_res=True, - ) diff --git a/documentdb_tests/compatibility/tests/system/administration/utils/__init__.py b/documentdb_tests/compatibility/tests/system/administration/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/documentdb_tests/compatibility/tests/system/administration/utils/administration_test_case.py b/documentdb_tests/compatibility/tests/system/administration/utils/administration_test_case.py new file mode 100644 index 000000000..0bb87e753 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/administration/utils/administration_test_case.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass, field +from typing import Any, Dict, List, Optional + +from documentdb_tests.framework.test_case import BaseTestCase + + +@dataclass(frozen=True) +class AdministrationTestCase(BaseTestCase): + """Test case for administration command tests. + + Administration commands often require runtime-discovered values (e.g. a + valid cluster parameter name). Fields that need these values accept a + callable that receives the discovered value at execution time. + + Attributes: + setup: Commands to run before the test command to establish state. + command: A callable ``(param: str) -> dict`` for commands that need a + runtime-discovered value, or a plain dict. + checks: Mapping of dotted field paths to property check objects, or a + callable that receives the same runtime values as ``build_command`` + and returns such a mapping. + """ + + setup: List[Dict[str, Any]] = field(default_factory=list) + command: Optional[Dict[str, Any] | Callable[..., Dict[str, Any]]] = None + checks: Dict[str, Any] | Callable[..., Dict[str, Any]] = field(default_factory=dict) + + def build_command(self, *args: Any, **kwargs: Any) -> Dict[str, Any]: + """Resolve the command dict from a callable or plain dict. + + Pass any runtime-discovered values as positional or keyword arguments; + they are forwarded to the callable unchanged. + """ + if self.command is None: + raise ValueError(f"AdministrationTestCase '{self.id}' has no command defined") + if callable(self.command): + return self.command(*args, **kwargs) + return self.command + + def build_checks(self, *args: Any, **kwargs: Any) -> Dict[str, Any]: + """Resolve the checks dict from a callable or plain dict. + + Pass the same runtime-discovered values used for ``build_command``. + """ + if callable(self.checks): + return self.checks(*args, **kwargs) + return self.checks diff --git a/documentdb_tests/framework/property_checks.py b/documentdb_tests/framework/property_checks.py index 0f029cb54..9c7b1f112 100644 --- a/documentdb_tests/framework/property_checks.py +++ b/documentdb_tests/framework/property_checks.py @@ -165,6 +165,25 @@ def __repr__(self) -> str: return f"{type(self).__name__}({self.expected!r})" +class LenGt(Check): + """Assert that the field is a list with length strictly greater than a minimum.""" + + def __init__(self, minimum: int) -> None: + self.minimum = minimum + + def check(self, value: Any, path: str) -> str | None: + if value is _FIELD_ABSENT: + return f"expected '{path}' length > {self.minimum}, but field is missing" + if not isinstance(value, list): + return f"expected '{path}' to be a list, got {type(value).__name__}" + if len(value) <= self.minimum: + return f"expected '{path}' length > {self.minimum}, got {len(value)}" + return None + + def __repr__(self) -> str: + return f"{type(self).__name__}({self.minimum!r})" + + class Contains(Check): """Assert that a list contains a dict where ``key`` equals ``value``.""" From 5bb4075790af12a471277ecc0ac38c749f6a75e4 Mon Sep 17 00:00:00 2001 From: PatersonProjects Date: Mon, 29 Jun 2026 14:43:57 -0700 Subject: [PATCH 5/5] Remove unused test class functions Signed-off-by: PatersonProjects --- ...t_getClusterParameter_argument_handling.py | 4 +- .../test_getClusterParameter_core_behavior.py | 4 +- .../test_getClusterParameter_errors.py | 2 +- ..._getClusterParameter_response_structure.py | 4 +- .../utils/administration_test_case.py | 41 +++---------------- 5 files changed, 12 insertions(+), 43 deletions(-) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py index 5fdde8516..3251bfafb 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_argument_handling.py @@ -88,5 +88,5 @@ def test_getClusterParameter_argument_rejects_type(collection, bson_type, sample @pytest.mark.parametrize("test", pytest_params(ARGUMENT_FORM_TESTS)) def test_getClusterParameter_argument_forms(collection, test): """Test accepted argument forms each return ok:1 with expected clusterParameters length.""" - result = execute_admin_command(collection, test.build_command()) - assertProperties(result, test.build_checks(), msg=test.msg, raw_res=True) + result = execute_admin_command(collection, test.command) + assertProperties(result, test.checks, msg=test.msg, raw_res=True) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py index 5e6cdaa96..10b317b59 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_core_behavior.py @@ -37,5 +37,5 @@ @pytest.mark.parametrize("test", pytest_params(CORE_BEHAVIOR_TESTS)) def test_getClusterParameter_core_behavior(collection, test): """Test core retrieval behavior: wildcard parameter count and inclusion.""" - result = execute_admin_command(collection, test.build_command()) - assertProperties(result, test.build_checks(), msg=test.msg, raw_res=True) + result = execute_admin_command(collection, test.command) + assertProperties(result, test.checks, msg=test.msg, raw_res=True) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py index a478241a4..3d5992c4f 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_errors.py @@ -122,7 +122,7 @@ @pytest.mark.parametrize("test", pytest_params(_ERROR_CASES)) def test_getClusterParameter_errors(collection, test): """Test all error-producing inputs: unknown names, type mismatches, and command routing.""" - result = execute_admin_command(collection, test.build_command()) + result = execute_admin_command(collection, test.command) assertFailureCode(result, test.error_code, msg=test.msg) diff --git a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py index eced7ed4e..2cd37a385 100644 --- a/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py +++ b/documentdb_tests/compatibility/tests/system/administration/commands/getClusterParameter/test_getClusterParameter_response_structure.py @@ -50,5 +50,5 @@ @pytest.mark.parametrize("test", pytest_params(PROPERTY_TESTS)) def test_getClusterParameter_response_properties(collection, test): """Verifies getClusterParameter response fields have expected types and values.""" - result = execute_admin_command(collection, test.build_command()) - assertProperties(result, test.build_checks(), msg=test.msg, raw_res=True) + result = execute_admin_command(collection, test.command) + assertProperties(result, test.checks, msg=test.msg, raw_res=True) diff --git a/documentdb_tests/compatibility/tests/system/administration/utils/administration_test_case.py b/documentdb_tests/compatibility/tests/system/administration/utils/administration_test_case.py index 0bb87e753..b9fbbd13c 100644 --- a/documentdb_tests/compatibility/tests/system/administration/utils/administration_test_case.py +++ b/documentdb_tests/compatibility/tests/system/administration/utils/administration_test_case.py @@ -1,8 +1,7 @@ from __future__ import annotations -from collections.abc import Callable from dataclasses import dataclass, field -from typing import Any, Dict, List, Optional +from typing import Any, Dict, Optional from documentdb_tests.framework.test_case import BaseTestCase @@ -11,40 +10,10 @@ class AdministrationTestCase(BaseTestCase): """Test case for administration command tests. - Administration commands often require runtime-discovered values (e.g. a - valid cluster parameter name). Fields that need these values accept a - callable that receives the discovered value at execution time. - Attributes: - setup: Commands to run before the test command to establish state. - command: A callable ``(param: str) -> dict`` for commands that need a - runtime-discovered value, or a plain dict. - checks: Mapping of dotted field paths to property check objects, or a - callable that receives the same runtime values as ``build_command`` - and returns such a mapping. + command: The command dict to execute. + checks: Mapping of dotted field paths to property check objects. """ - setup: List[Dict[str, Any]] = field(default_factory=list) - command: Optional[Dict[str, Any] | Callable[..., Dict[str, Any]]] = None - checks: Dict[str, Any] | Callable[..., Dict[str, Any]] = field(default_factory=dict) - - def build_command(self, *args: Any, **kwargs: Any) -> Dict[str, Any]: - """Resolve the command dict from a callable or plain dict. - - Pass any runtime-discovered values as positional or keyword arguments; - they are forwarded to the callable unchanged. - """ - if self.command is None: - raise ValueError(f"AdministrationTestCase '{self.id}' has no command defined") - if callable(self.command): - return self.command(*args, **kwargs) - return self.command - - def build_checks(self, *args: Any, **kwargs: Any) -> Dict[str, Any]: - """Resolve the checks dict from a callable or plain dict. - - Pass the same runtime-discovered values used for ``build_command``. - """ - if callable(self.checks): - return self.checks(*args, **kwargs) - return self.checks + command: Optional[Dict[str, Any]] = None + checks: Dict[str, Any] = field(default_factory=dict)