-
Notifications
You must be signed in to change notification settings - Fork 33
Add comprehensive tests for the query-and-write insert command #597
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
eerxuan
merged 13 commits into
documentdb:main
from
imforster:forstaia/insert/query-and-write
Jun 16, 2026
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
95b3633
Add comprehensive tests for the query-and-write insert command
imforster f3890cf
Add comprehensive tests for the query-and-write insert command
imforster 5346ce4
Fix plain assert in test_insert_id_handling causing unit test validat…
imforster 97f649a
Use Binary wrapper for BIN_DATA in BSON_TYPE_SAMPLES
imforster 55d24d3
Update command_test_case import path after main moved it
imforster 599d86f
Rename test_insert_at_max_write_batch_size to test_insert_large_batch…
imforster fbbaca6
Remove test_insert_dbref_fields
imforster 8ea41e5
Move insert-specific tests from test_insert_bson_type_preservation
imforster 33df29b
Delete test_insert_bson_type_preservation.py
imforster da619c2
Trim namespace validation matrix to 1 representative case (§19)
imforster 0f7f6fd
Remove insert-duplicated coverage per reviewer feedback
imforster db4a9e6
Add Code type to custom _id tests and NaN _id behavior tests
imforster 3acdb55
Merge branch 'main' into forstaia/insert/query-and-write
eerxuan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
206 changes: 206 additions & 0 deletions
206
...compatibility/tests/core/query_and_write/commands/insert/test_insert_argument_handling.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| """Tests for insert command field type validation.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from dataclasses import dataclass | ||
| from typing import Any | ||
|
|
||
| import pytest | ||
| from bson import Decimal128, Int64, MaxKey, MinKey, Regex | ||
| from bson.binary import Binary | ||
|
|
||
| from documentdb_tests.framework.assertions import ( | ||
| assertExceptionType, | ||
| assertResult, | ||
| assertSuccessPartial, | ||
| ) | ||
| from documentdb_tests.framework.error_codes import ( | ||
| BAD_VALUE_ERROR, | ||
| INVALID_NAMESPACE_ERROR, | ||
| MISSING_FIELD_ERROR, | ||
| TYPE_MISMATCH_ERROR, | ||
| UNRECOGNIZED_COMMAND_FIELD_ERROR, | ||
| ) | ||
| from documentdb_tests.framework.executor import 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 DATE_EPOCH, OID_EPOCH, TS_EPOCH | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class FieldValidationTest(BaseTestCase): | ||
| """Test case for insert command field validation errors.""" | ||
|
|
||
| command: Any = None | ||
|
|
||
|
|
||
| # Property [Command Field Rejection]: insert command rejects non-string types for the | ||
| # collection name field, and rejects missing or empty documents array. | ||
| # Wire-protocol namespace validation (INVALID_NAMESPACE_ERROR for non-string types) is | ||
| # foundational behavior per TEST_COVERAGE.md §19. One representative case wires insert | ||
| # to that behavior; the full type matrix belongs in the centralized namespace test site | ||
| # (currently TBD — see TEST_COVERAGE.md §19). | ||
| TESTS: list[FieldValidationTest] = [ | ||
| FieldValidationTest( | ||
| "insert_field_rejects_non_string", | ||
| command={"insert": 1, "documents": [{"_id": 1}]}, | ||
| error_code=INVALID_NAMESPACE_ERROR, | ||
| msg="insert should reject non-string type for collection name field.", | ||
| ), | ||
| FieldValidationTest( | ||
| "empty_string_collection_name", | ||
| command=None, # Needs collection.name substitution | ||
| error_code=INVALID_NAMESPACE_ERROR, | ||
| msg="insert should reject empty string collection name.", | ||
| ), | ||
| FieldValidationTest( | ||
| "missing_documents_field", | ||
| command=None, | ||
| error_code=MISSING_FIELD_ERROR, | ||
| msg="insert should reject missing documents field.", | ||
| ), | ||
| FieldValidationTest( | ||
| "negative_maxtimems", | ||
| command=None, | ||
| error_code=BAD_VALUE_ERROR, | ||
| msg="insert should reject negative maxTimeMS.", | ||
| ), | ||
| ] | ||
|
|
||
| _DYNAMIC_COMMANDS: dict[str, Any] = { | ||
| "empty_string_collection_name": lambda n: {"insert": "", "documents": [{"_id": 1}]}, | ||
| "missing_documents_field": lambda n: {"insert": n}, | ||
| "negative_maxtimems": lambda n: {"insert": n, "documents": [{"_id": 1}], "maxTimeMS": -1}, | ||
| } | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("test", pytest_params(TESTS)) | ||
| def test_insert_field_validation(collection, test: FieldValidationTest): | ||
| """Test insert command rejects invalid field types.""" | ||
| if test.command is not None: | ||
| cmd = test.command | ||
| else: | ||
| cmd = _DYNAMIC_COMMANDS[test.id](collection.name) | ||
| result = execute_command(collection, cmd) | ||
| assertResult(result, error_code=test.error_code, msg=test.msg) | ||
|
|
||
|
|
||
| # Property [Documents Element Rejection]: insert rejects non-document elements in the | ||
| # documents array (driver-side validation). | ||
| # Property [Documents Type Rejection]: insert rejects non-array types for the documents field. | ||
| # Both produce driver-side exceptions before reaching the server. | ||
| _DOCUMENTS_INVALID_TYPE_PARAMS = [ | ||
| pytest.param(1, id="int"), | ||
| pytest.param(3.14, id="double"), | ||
| pytest.param(True, id="bool"), | ||
| pytest.param(None, id="null"), | ||
| pytest.param({"a": 1}, id="object"), | ||
| pytest.param("hello", id="string"), | ||
| pytest.param(OID_EPOCH, id="objectId"), | ||
| pytest.param(DATE_EPOCH, id="date"), | ||
| pytest.param(Regex(".*"), id="regex"), | ||
| pytest.param(TS_EPOCH, id="timestamp"), | ||
| pytest.param(Binary(b"\x01"), id="binary"), | ||
| pytest.param(Decimal128("1"), id="decimal128"), | ||
| pytest.param(MinKey(), id="minKey"), | ||
| pytest.param(MaxKey(), id="maxKey"), | ||
| ] | ||
|
|
||
|
|
||
| @pytest.mark.insert | ||
| @pytest.mark.parametrize("docs_value", _DOCUMENTS_INVALID_TYPE_PARAMS) | ||
| def test_insert_documents_field_rejects_invalid_type(collection, docs_value): | ||
| """Test that insert rejects non-array types for the documents field.""" | ||
| result = execute_command(collection, {"insert": collection.name, "documents": docs_value}) | ||
| assertExceptionType(result, Exception, msg="insert should reject non-array documents field.") | ||
|
|
||
|
|
||
| # Property [Documents Array Element Rejection]: insert rejects non-document elements in | ||
| # the documents array, including int, string, null, and array elements. | ||
| _DOCUMENTS_INVALID_ELEMENT_PARAMS = [ | ||
| pytest.param(1, id="int"), | ||
| pytest.param("hello", id="string"), | ||
| pytest.param(None, id="null"), | ||
| pytest.param([1, 2], id="array"), | ||
| ] | ||
|
|
||
|
|
||
| @pytest.mark.insert | ||
| @pytest.mark.parametrize("element", _DOCUMENTS_INVALID_ELEMENT_PARAMS) | ||
| def test_insert_documents_array_rejects_invalid_element(collection, element): | ||
| """Test that insert rejects non-document elements in the documents array.""" | ||
| result = execute_command(collection, {"insert": collection.name, "documents": [element]}) | ||
| assertExceptionType( | ||
| result, Exception, msg="insert should reject non-document element in documents array." | ||
| ) | ||
|
|
||
|
|
||
| # Property [Comment Acceptance]: insert accepts any BSON type for the comment field. | ||
| COMMENT_TYPES = [ | ||
| pytest.param("a comment", id="string"), | ||
| pytest.param(42, id="int"), | ||
| pytest.param(True, id="bool"), | ||
| pytest.param(None, id="null"), | ||
| pytest.param({"k": "v"}, id="object"), | ||
| pytest.param([1, 2], id="array"), | ||
| ] | ||
|
|
||
|
|
||
| @pytest.mark.insert | ||
| @pytest.mark.parametrize("comment", COMMENT_TYPES) | ||
| def test_insert_comment_accepts_any_type(collection, comment): | ||
| """Test that comment field accepts any BSON type.""" | ||
| result = execute_command( | ||
| collection, | ||
| {"insert": collection.name, "documents": [{"_id": 1}], "comment": comment}, | ||
| ) | ||
| assertSuccessPartial(result, {"ok": 1.0, "n": 1}, msg="insert should accept any comment type.") | ||
|
|
||
|
|
||
| # Property [Ordered Type Rejection]: insert rejects non-boolean-coercible types for ordered. | ||
| # null is accepted (treated as default). Numeric types and all non-bool non-null types | ||
| # produce TYPE_MISMATCH_ERROR. | ||
| _ORDERED_INVALID_PARAMS = [ | ||
| pytest.param({"ordered": "true"}, id="string"), | ||
| pytest.param({"ordered": 1}, id="int"), | ||
| pytest.param({"ordered": 1.0}, id="double"), | ||
| pytest.param({"ordered": Int64(1)}, id="int64"), | ||
| pytest.param({"ordered": Decimal128("1")}, id="decimal128"), | ||
| pytest.param({"ordered": {"v": True}}, id="object"), | ||
| pytest.param({"ordered": [True]}, id="array"), | ||
| pytest.param({"ordered": DATE_EPOCH}, id="date"), | ||
| pytest.param({"ordered": OID_EPOCH}, id="objectId"), | ||
| pytest.param({"ordered": Regex(".*")}, id="regex"), | ||
| pytest.param({"ordered": TS_EPOCH}, id="timestamp"), | ||
| pytest.param({"ordered": Binary(b"\x01")}, id="binary"), | ||
| pytest.param({"ordered": MinKey()}, id="minKey"), | ||
| pytest.param({"ordered": MaxKey()}, id="maxKey"), | ||
| ] | ||
|
|
||
|
|
||
| @pytest.mark.insert | ||
| @pytest.mark.parametrize("extra_fields", _ORDERED_INVALID_PARAMS) | ||
| def test_insert_ordered_rejects_invalid_type(collection, extra_fields): | ||
| """Test insert rejects non-boolean ordered field.""" | ||
| cmd = {"insert": collection.name, "documents": [{"_id": 1}], **extra_fields} | ||
| result = execute_command(collection, cmd) | ||
| assertResult( | ||
| result, | ||
| error_code=TYPE_MISMATCH_ERROR, | ||
| msg="insert should reject non-boolean ordered field.", | ||
| ) | ||
|
|
||
|
|
||
| # Property [Unrecognized Field Rejection]: insert rejects unknown top-level command fields. | ||
| @pytest.mark.insert | ||
| def test_insert_rejects_unrecognized_field(collection): | ||
| """Test insert rejects unrecognized top-level fields.""" | ||
| result = execute_command( | ||
| collection, | ||
| {"insert": collection.name, "documents": [{"_id": 1}], "unknownField": 1}, | ||
| ) | ||
| assertResult( | ||
| result, | ||
| error_code=UNRECOGNIZED_COMMAND_FIELD_ERROR, | ||
| msg="insert should reject unrecognized fields.", | ||
| ) | ||
69 changes: 69 additions & 0 deletions
69
...compatibility/tests/core/query_and_write/commands/insert/test_insert_batch_size_limits.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| """ | ||
| Insert batch size limit tests. | ||
|
|
||
| Tests maxWriteBatchSize enforcement and document size rejection. | ||
| """ | ||
|
|
||
| import pytest | ||
|
|
||
| from documentdb_tests.compatibility.tests.core.utils.command_test_case import ( | ||
| CommandContext, | ||
| CommandTestCase, | ||
| ) | ||
| from documentdb_tests.framework.assertions import assertResult, assertSuccessPartial | ||
| from documentdb_tests.framework.error_codes import BSON_OBJECT_TOO_LARGE_ERROR, INVALID_LENGTH_ERROR | ||
| from documentdb_tests.framework.executor import execute_command | ||
| from documentdb_tests.framework.parametrize import pytest_params | ||
|
|
||
| # Property [Document and Batch Rejection]: insert rejects an empty documents array | ||
| # and a document exceeding the BSON size limit. | ||
| BATCH_LIMIT_REJECTED_TESTS: list[CommandTestCase] = [ | ||
| CommandTestCase( | ||
| "empty_documents_array", | ||
| command=lambda ctx: {"insert": ctx.collection, "documents": []}, | ||
| error_code=INVALID_LENGTH_ERROR, | ||
| msg="insert should reject an empty documents array.", | ||
| ), | ||
| CommandTestCase( | ||
| "document_exceeds_size_limit", | ||
| # A string value of 16MB plus document overhead exceeds the size limit. | ||
| command=lambda ctx: { | ||
| "insert": ctx.collection, | ||
| "documents": [{"_id": 1, "value": "x" * (16 * 1024 * 1024)}], | ||
| }, | ||
| error_code=BSON_OBJECT_TOO_LARGE_ERROR, | ||
| msg="insert should reject a document exceeding the BSON size limit.", | ||
| ), | ||
| ] | ||
|
|
||
|
|
||
| @pytest.mark.insert | ||
| @pytest.mark.parametrize("test", pytest_params(BATCH_LIMIT_REJECTED_TESTS)) | ||
| def test_insert_batch_limit_rejected(collection, test: CommandTestCase): | ||
| """Test insert rejects empty batch and oversized documents.""" | ||
| collection = test.prepare(collection.database, collection) | ||
| ctx = CommandContext.from_collection(collection) | ||
| result = execute_command(collection, test.build_command(ctx)) | ||
| assertResult(result, error_code=test.error_code, msg=test.msg) | ||
|
|
||
|
|
||
| @pytest.mark.insert | ||
| def test_insert_large_batch_succeeds(collection): | ||
| """Test insert succeeds with a large batch of documents. | ||
|
|
||
| This is a wiring test: it confirms the server accepts multi-document | ||
| inserts without error. It does not test the maxWriteBatchSize boundary | ||
| because inserting up to 100,000 documents is impractical in a functional | ||
| test suite. See the Property [Document and Batch Rejection] cases above | ||
| for rejection behavior. | ||
| """ | ||
| docs = [{"_id": i} for i in range(1000)] | ||
| result = execute_command( | ||
| collection, | ||
| {"insert": collection.name, "documents": docs}, | ||
| ) | ||
| assertSuccessPartial( | ||
| result, | ||
| {"ok": 1.0, "n": 1000}, | ||
| msg="insert should succeed with a batch of 1000 documents.", | ||
| ) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.