Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
"""Tests for validate command 'checkBSONConformance' parameter type coercion.

Validates that the checkBSONConformance parameter accepts all BSON types via
coercion.
"""

from __future__ import annotations

from datetime import datetime, timezone

import pytest
from bson import Binary, Code, Decimal128, Int64, MaxKey, MinKey, ObjectId, Regex, Timestamp

from documentdb_tests.compatibility.tests.system.diagnostic.utils.diagnostic_test_case import (
DiagnosticTestCase,
)
from documentdb_tests.framework.assertions import assertProperties
from documentdb_tests.framework.executor import execute_command
from documentdb_tests.framework.parametrize import pytest_params
from documentdb_tests.framework.property_checks import Eq

# Property [Type Coercion]: validate accepts all BSON types for the
# checkBSONConformance parameter via coercion.
ACCEPTED_TYPE_TESTS: list[DiagnosticTestCase] = [
DiagnosticTestCase(
"bool_true",
command={"checkBSONConformance": True},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept bool true",
),
DiagnosticTestCase(
"bool_false",
command={"checkBSONConformance": False},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept bool false",
),
DiagnosticTestCase(
"int32_1",
command={"checkBSONConformance": 1},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept int32 1 (coerces to true)",
),
DiagnosticTestCase(
"int32_0",
command={"checkBSONConformance": 0},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept int32 0 (coerces to false)",
),
DiagnosticTestCase(
"double_1",
command={"checkBSONConformance": 1.0},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept double 1.0 (coerces to true)",
),
DiagnosticTestCase(
"double_0",
command={"checkBSONConformance": 0.0},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept double 0.0 (coerces to false)",
),
DiagnosticTestCase(
"int64_1",
command={"checkBSONConformance": Int64(1)},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept Int64(1) (coerces to true)",
),
DiagnosticTestCase(
"int64_0",
command={"checkBSONConformance": Int64(0)},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept Int64(0) (coerces to false)",
),
DiagnosticTestCase(
"decimal128_1",
command={"checkBSONConformance": Decimal128("1")},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept Decimal128('1') (coerces to true)",
),
DiagnosticTestCase(
"decimal128_0",
command={"checkBSONConformance": Decimal128("0")},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept Decimal128('0') (coerces to false)",
),
DiagnosticTestCase(
"null",
command={"checkBSONConformance": None},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept null (treated as omitted/false)",
),
DiagnosticTestCase(
"string",
command={"checkBSONConformance": "true"},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept string (coerces to truthy)",
),
DiagnosticTestCase(
"object",
command={"checkBSONConformance": {}},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept object (coerces to truthy)",
),
DiagnosticTestCase(
"array",
command={"checkBSONConformance": []},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept array (coerces to truthy)",
),
DiagnosticTestCase(
"binary",
command={"checkBSONConformance": Binary(b"")},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept Binary (coerces to truthy)",
),
DiagnosticTestCase(
"objectid",
command={"checkBSONConformance": ObjectId()},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept ObjectId (coerces to truthy)",
),
DiagnosticTestCase(
"datetime",
command={"checkBSONConformance": datetime(2024, 1, 1, tzinfo=timezone.utc)},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept datetime (coerces to truthy)",
),
DiagnosticTestCase(
"regex",
command={"checkBSONConformance": Regex(".*")},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept Regex (coerces to truthy)",
),
DiagnosticTestCase(
"timestamp",
command={"checkBSONConformance": Timestamp(0, 0)},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept Timestamp (coerces to truthy)",
),
DiagnosticTestCase(
"code",
command={"checkBSONConformance": Code("function(){}")},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept JavaScript Code (coerces to truthy)",
),
DiagnosticTestCase(
"minkey",
command={"checkBSONConformance": MinKey()},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept MinKey (coerces to truthy)",
),
DiagnosticTestCase(
"maxkey",
command={"checkBSONConformance": MaxKey()},
checks={"ok": Eq(1.0)},
msg="checkBSONConformance should accept MaxKey (coerces to truthy)",
),
]


@pytest.mark.parametrize("test", pytest_params(ACCEPTED_TYPE_TESTS))
def test_validate_checkBSONConformance_accepted_types(collection, test):
"""Test that validate accepts all BSON types for checkBSONConformance."""
collection.insert_one({"_id": 1})
result = execute_command(
collection,
{"validate": collection.name, **test.command},
)
assertProperties(result, test.checks, msg=test.msg, raw_res=True)
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
"""Tests for validate command core behavior.

Validates basic functionality, counts, consistency across calls, comment parameter,
behavior on different collection types (capped, timeseries, clustered), and valid
option combinations.
"""

from __future__ import annotations

from datetime import datetime, timezone

import pytest

from documentdb_tests.compatibility.tests.system.diagnostic.utils.diagnostic_test_case import (
DiagnosticTestCase,
)
from documentdb_tests.framework.assertions import assertProperties, assertSuccessPartial
from documentdb_tests.framework.executor import execute_command
from documentdb_tests.framework.parametrize import pytest_params
from documentdb_tests.framework.property_checks import Eq

# Property [Core Behavior]: validate returns expected results for populated
# and empty collections and supports common parameters.
CORE_BEHAVIOR_TESTS: list[DiagnosticTestCase] = [
DiagnosticTestCase(
"populated_collection",
setup=[{"insert": "", "documents": [{"_id": i, "x": i} for i in range(5)]}],
checks={"ok": Eq(1.0), "valid": Eq(True), "nrecords": Eq(5)},
msg="validate should return valid: true with correct nrecords for a populated collection",
),
DiagnosticTestCase(
"empty_collection",
setup=[{"create": ""}],
checks={
"ok": Eq(1.0),
"valid": Eq(True),
"nrecords": Eq(0),
"nIndexes": Eq(1),
},
msg="validate should return nrecords: 0 and nIndexes: 1 for an empty collection",
),
DiagnosticTestCase(
"after_insert_and_delete_all",
setup=[
{"insert": "", "documents": [{"_id": i} for i in range(5)]},
{"delete": "", "deletes": [{"q": {}, "limit": 0}]},
],
checks={"ok": Eq(1.0), "valid": Eq(True), "nrecords": Eq(0)},
msg="validate should return nrecords: 0 after deleting all documents",
),
DiagnosticTestCase(
"after_dropping_indexes",
setup=[
{"insert": "", "documents": [{"_id": 1, "x": 1}]},
{"createIndexes": "", "indexes": [{"key": {"x": 1}, "name": "x_1"}]},
{"dropIndexes": "", "index": "*"},
],
checks={"ok": Eq(1.0), "nIndexes": Eq(1)},
msg="validate should return nIndexes: 1 after dropping secondary indexes",
),
DiagnosticTestCase(
"with_comment",
setup=[{"insert": "", "documents": [{"_id": 1}]}],
command={"comment": "test comment"},
checks={"ok": Eq(1.0)},
msg="validate should succeed with comment parameter",
),
DiagnosticTestCase(
"unrecognized_field_ignored",
setup=[{"insert": "", "documents": [{"_id": 1}]}],
command={"unknownField": 1},
checks={"ok": Eq(1.0), "valid": Eq(True)},
msg="validate should ignore unrecognized fields and succeed",
),
]


@pytest.mark.parametrize("test", pytest_params(CORE_BEHAVIOR_TESTS))
def test_validate_core_behavior(collection, test):
"""Test validate core behavior with various collection states."""
for cmd in test.setup:
execute_command(collection, {**cmd, next(iter(cmd)): collection.name})
cmd = {"validate": collection.name}
if test.command:
cmd.update(test.command)
result = execute_command(collection, cmd)
assertProperties(result, test.checks, msg=test.msg, raw_res=True)


def test_validate_consistent_across_calls(collection):
"""Test validate returns consistent results across multiple calls."""
collection.insert_many([{"_id": i, "x": i} for i in range(5)])
result1 = execute_command(collection, {"validate": collection.name})
result2 = execute_command(collection, {"validate": collection.name})
assertProperties(
result1,
{
"nrecords": Eq(result2["nrecords"]),
"nIndexes": Eq(result2["nIndexes"]),
"valid": Eq(result2["valid"]),
},
raw_res=True,
msg="validate should return identical key fields across consecutive calls",
)


def test_validate_reflects_modifications(collection):
"""Test validate reflects modifications between calls."""
collection.insert_many([{"_id": i} for i in range(3)])
execute_command(collection, {"validate": collection.name})
collection.insert_many([{"_id": i} for i in range(3, 8)])
result2 = execute_command(collection, {"validate": collection.name})
assertProperties(
result2,
{"nrecords": Eq(8)},
raw_res=True,
msg="validate should reflect updated nrecords after additional inserts",
)


def test_validate_capped_collection(database_client, collection):
"""Test validate on a capped collection succeeds."""
coll_name = f"{collection.name}_capped"
database_client.create_collection(coll_name, capped=True, size=1_048_576)
coll = database_client[coll_name]
coll.insert_one({"_id": 1, "x": 1})
result = execute_command(coll, {"validate": coll.name})
assertSuccessPartial(
result,
{"ok": 1.0, "valid": True},
msg="validate should succeed on a capped collection",
)


def test_validate_timeseries_collection(database_client, collection):
"""Test validate on a time series collection succeeds."""
coll_name = f"{collection.name}_timeseries"
database_client.create_collection(
coll_name,
timeseries={"timeField": "ts", "metaField": "meta"},
)
coll = database_client[coll_name]
coll.insert_one({"ts": datetime(2024, 1, 1, tzinfo=timezone.utc), "meta": "a", "v": 1})
result = execute_command(coll, {"validate": coll.name})
assertSuccessPartial(
result,
{"ok": 1.0},
msg="validate should succeed on a timeseries collection",
)


def test_validate_clustered_collection(database_client, collection):
"""Test validate on a clustered collection succeeds."""
coll_name = f"{collection.name}_clustered"
database_client.command(
"create",
coll_name,
clusteredIndex={"key": {"_id": 1}, "unique": True},
)
coll = database_client[coll_name]
coll.insert_one({"_id": 1, "x": 1})
result = execute_command(coll, {"validate": coll.name})
assertSuccessPartial(
result,
{"ok": 1.0},
msg="validate should succeed on a clustered collection",
)


# Property [Valid Options]: validate succeeds with each option individually
# and with compatible multi-option combinations.
VALID_OPTION_TESTS: list[DiagnosticTestCase] = [
DiagnosticTestCase(
"all_options_false",
command={"full": False, "repair": False, "metadata": False, "checkBSONConformance": False},
checks={"ok": Eq(1.0)},
msg="validate should succeed with all options set to false explicitly",
),
DiagnosticTestCase(
"full_true",
command={"full": True},
checks={"ok": Eq(1.0)},
msg="validate with full: true should succeed",
),
DiagnosticTestCase(
"checkBSONConformance_true",
command={"checkBSONConformance": True},
checks={"ok": Eq(1.0)},
msg="validate with checkBSONConformance: true should succeed",
),
DiagnosticTestCase(
"full_with_checkBSONConformance",
command={"full": True, "checkBSONConformance": True},
checks={"ok": Eq(1.0)},
msg="validate with full: true and checkBSONConformance: true should succeed",
),
DiagnosticTestCase(
"metadata_true",
command={"metadata": True},
checks={"ok": Eq(1.0)},
msg="validate with metadata: true should succeed",
),
DiagnosticTestCase(
"fixMultikey_true",
command={"fixMultikey": True},
checks={"ok": Eq(1.0)},
msg="validate with fixMultikey: true should succeed",
),
DiagnosticTestCase(
"repair_true",
command={"repair": True},
checks={"ok": Eq(1.0)},
msg="validate with repair: true should succeed",
),
DiagnosticTestCase(
"repair_with_fixMultikey",
command={"repair": True, "fixMultikey": True},
checks={"ok": Eq(1.0)},
msg="validate with repair: true and fixMultikey: true should succeed",
),
]


@pytest.mark.parametrize("test", pytest_params(VALID_OPTION_TESTS))
def test_validate_valid_options(collection, test):
"""Test that validate succeeds with valid option values."""
collection.insert_one({"_id": 1})
result = execute_command(collection, {"validate": collection.name, **test.command})
assertProperties(result, test.checks, msg=test.msg, raw_res=True)
Loading
Loading