From 9b83e93e8722c146dbe586f2016e7f633fac64b8 Mon Sep 17 00:00:00 2001 From: Yunxuan Shi Date: Thu, 6 Aug 2026 16:16:19 -0700 Subject: [PATCH 1/2] Remove $documents dependency from expression test helpers The expression compatibility tests use a collectionless `{aggregate: 1}` + `$documents: [{}]` pipeline purely as a scaffold to feed a single empty document into $project. They do not test $documents itself, yet they inherit a hard dependency on $documents support from the shared helpers. Swap the scaffold for `collection.insert_one({})` plus an aggregate over the named collection. This is logically identical: both feed exactly one empty document to $project, so literal expressions and field references (which resolve to missing against an empty doc) behave the same. The `collection` fixture is function-scoped, so each test still runs against a fresh single-document collection. Updates the two shared helpers (execute_expression, execute_project) that ~205 files inherit, plus two inline call sites in test_expressions_combination_variables.py. The _with_insert sibling helpers are unchanged. Signed-off-by: Yunxuan Shi --- .../test_expressions_combination_variables.py | 8 +++--- .../core/operator/expressions/utils/utils.py | 25 +++++++++++++------ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/test_expressions_combination_variables.py b/documentdb_tests/compatibility/tests/core/operator/expressions/test_expressions_combination_variables.py index ff55a776f..43882418e 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/test_expressions_combination_variables.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/test_expressions_combination_variables.py @@ -160,12 +160,12 @@ def test_let_nested_combinations(collection, test): # --------------------------------------------------------------------------- def test_let_two_lets_same_projection(collection): """Test two separate $let expressions in same projection with same variable name.""" + collection.insert_one({}) result = execute_command( collection, { - "aggregate": 1, + "aggregate": collection.name, "pipeline": [ - {"$documents": [{}]}, { "$project": { "_id": 0, @@ -248,12 +248,12 @@ def test_let_across_multiple_documents(collection): def test_let_error_cross_let_variable_ref(collection): """Test $let where variable defined in one $let is referenced in sibling $let.""" + collection.insert_one({}) result = execute_command( collection, { - "aggregate": 1, + "aggregate": collection.name, "pipeline": [ - {"$documents": [{}]}, { "$project": { "_id": 0, diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/utils/utils.py b/documentdb_tests/compatibility/tests/core/operator/expressions/utils/utils.py index ec6d21c75..f4abbae74 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/utils/utils.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/utils/utils.py @@ -39,6 +39,12 @@ def execute_project(collection, project): """ Execute a projection with literal input values. + Evaluates the projection against a single empty document. The document is + inserted into the collection and the pipeline runs over that collection, + rather than synthesizing the row with a ``$documents`` stage. This keeps the + helper free of any dependency on ``$documents`` support while producing the + same single-row input the projection sees. + Args: collection: MongoDB collection object project: Fields to project. Do not include _id; the function always @@ -51,12 +57,12 @@ def execute_project(collection, project): >>> execute_project(collection, {"sum": {"$add": [1, 2]}}) # Returns result with {"sum": 3} in firstBatch """ + collection.insert_one({}) return execute_command( collection, { - "aggregate": 1, + "aggregate": collection.name, "pipeline": [ - {"$documents": [{}]}, {"$project": {**materialize(project), "_id": 0}}, ], "cursor": {}, @@ -100,10 +106,15 @@ def execute_project_with_insert(collection, document, project): def execute_expression(collection, expression): """ - Execute an aggregation expression using $documents stage. + Execute an aggregation expression against a single empty document. - Evaluates an expression against an empty document using the $documents - stage. Useful for testing expressions with literal values. + Evaluates an expression against an empty document. The document is inserted + into the collection and the pipeline runs over that collection, rather than + synthesizing the row with a ``$documents`` stage. This keeps the helper free + of any dependency on ``$documents`` support while producing the same + single-row input the expression is evaluated against. Useful for testing + expressions with literal values; field references resolve to missing, just + as they would against a ``$documents: [{}]`` row. Args: collection: MongoDB collection object @@ -117,12 +128,12 @@ def execute_expression(collection, expression): >>> execute_expression(collection, {"$add": [1, 2]}) # Returns result with {"result": 3} in firstBatch """ + collection.insert_one({}) return execute_command( collection, { - "aggregate": 1, + "aggregate": collection.name, "pipeline": [ - {"$documents": [{}]}, {"$project": {"_id": 0, "result": expression}}, ], "cursor": {}, From 2ee45c5aa1b0efc40c21c4f74e12cffea1f6fbdc Mon Sep 17 00:00:00 2001 From: Yunxuan Shi Date: Thu, 6 Aug 2026 18:22:11 -0700 Subject: [PATCH 2/2] Fix $$ROOT/$$NOW system-variable tests broken by helper change The $documents-removal commit made execute_expression/execute_project insert a document and aggregate over the whole collection. Two newly synced system-variable tests relied on the old $documents:[{}] contract and broke: - test_root_empty_document: needs a truly field-less input so $$ROOT is {}, but an inserted doc always carries an auto _id. It now shapes its own pipeline ($replaceWith:{$literal:{}}) instead of the shared helper. - test_now_identical_across_getmore_batches: pre-loads 300 docs, so the whole-collection helper emitted 300 rows. It now uses an inline pipeline with $limit:1 to collapse to the single expected row. The shared helpers stay on plain insert_one({}) so the ~359 literal expression call sites gain no $replaceWith/$limit dependency. Signed-off-by: Yunxuan Shi --- .../core/operator/expressions/utils/utils.py | 14 ++++++ .../now/test_now_core_semantics.py | 19 ++++++-- .../root/test_root_core_behavior.py | 43 +++++++++++-------- 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/utils/utils.py b/documentdb_tests/compatibility/tests/core/operator/expressions/utils/utils.py index f4abbae74..f8d45ce5e 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/utils/utils.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/utils/utils.py @@ -45,6 +45,13 @@ def execute_project(collection, project): helper free of any dependency on ``$documents`` support while producing the same single-row input the projection sees. + Note: the inserted document carries an auto-generated ``_id`` and the helper + aggregates over the whole collection. The output projection excludes ``_id``, + so literal expressions and missing-field references behave identically to a + ``$documents: [{}]`` row. Callers that need a truly field-less input (e.g. + ``$$ROOT`` must be ``{}``) or exactly one row over a pre-populated collection + must shape their own pipeline instead of using this helper. + Args: collection: MongoDB collection object project: Fields to project. Do not include _id; the function always @@ -116,6 +123,13 @@ def execute_expression(collection, expression): expressions with literal values; field references resolve to missing, just as they would against a ``$documents: [{}]`` row. + Note: the inserted document carries an auto-generated ``_id`` and the helper + aggregates over the whole collection. The output projection excludes ``_id``, + so literal expressions and missing-field references are unaffected. Callers + that need a truly field-less input (e.g. ``$$ROOT`` must be ``{}``) or exactly + one row over a pre-populated collection must shape their own pipeline instead + of using this helper. + Args: collection: MongoDB collection object expression: The expression to evaluate (e.g., {"$add": [1, 2]}) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/variable/system_variables/now/test_now_core_semantics.py b/documentdb_tests/compatibility/tests/core/operator/expressions/variable/system_variables/now/test_now_core_semantics.py index 8dc17e8ec..bf1db1b59 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/variable/system_variables/now/test_now_core_semantics.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/variable/system_variables/now/test_now_core_semantics.py @@ -198,10 +198,23 @@ def test_now_identical_across_getmore_batches(collection): seen.extend(doc["t"] for doc in batch["cursor"]["nextBatch"]) cursor_id = batch["cursor"]["id"] - result = execute_expression(collection, {"$size": {"$setUnion": [seen]}}) - assert_expression_result( + # The collection is pre-populated (300 docs), so ``execute_expression`` — which + # aggregates over the whole collection — would emit one row per document. A + # ``$limit: 1`` reduces it to the single row this assertion expects. + result = execute_command( + collection, + { + "aggregate": collection.name, + "pipeline": [ + {"$limit": 1}, + {"$project": {"_id": 0, "result": {"$size": {"$setUnion": [seen]}}}}, + ], + "cursor": {}, + }, + ) + assertSuccess( result, - expected=1, + [{"result": 1}], msg="$$NOW should be identical across every getMore batch of one cursor", ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/variable/system_variables/root/test_root_core_behavior.py b/documentdb_tests/compatibility/tests/core/operator/expressions/variable/system_variables/root/test_root_core_behavior.py index 22ed192ed..38b93ab90 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/variable/system_variables/root/test_root_core_behavior.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/variable/system_variables/root/test_root_core_behavior.py @@ -17,10 +17,10 @@ ) from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( assert_expression_result, - execute_expression, execute_expression_with_insert, ) from documentdb_tests.framework.assertions import assertSuccess +from documentdb_tests.framework.executor import execute_command from documentdb_tests.framework.parametrize import pytest_params from documentdb_tests.framework.test_constants import DOUBLE_PRECISION_LOSS, INT64_MAX @@ -122,27 +122,32 @@ def test_root_echoes_doc(collection, test): # Property [Empty Document]: $$ROOT is an empty object when the input document has # no fields. -ROOT_EMPTY_DOCUMENT_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - id="empty_document", - expression="$$ROOT", - doc=None, - expected={}, - msg="$$ROOT should return an empty object when the input document has no fields", - ), -] - - -@pytest.mark.parametrize("test", pytest_params(ROOT_EMPTY_DOCUMENT_TESTS)) -def test_root_empty_document(collection, test): +def test_root_empty_document(collection): """$$ROOT over a field-less input document. - ``doc=None`` selects execute_expression, which evaluates the expression over - a ``$documents: [{}]`` stage rather than inserting a document, since an - inserted document would always be given an ``_id``. + This case needs a truly field-less input row, so it cannot use the shared + ``execute_expression`` helper: that helper inserts a document (which always + carries an auto-generated ``_id``), which would make ``$$ROOT`` a one-field + document. Instead a document is inserted and ``$replaceWith: {$literal: {}}`` + strips it back to a field-less row before ``$$ROOT`` is read. """ - result = execute_expression(collection, test.expression) - assert_expression_result(result, expected=test.expected, msg=test.msg) + collection.insert_one({}) + result = execute_command( + collection, + { + "aggregate": collection.name, + "pipeline": [ + {"$replaceWith": {"$literal": {}}}, + {"$project": {"_id": 0, "result": "$$ROOT"}}, + ], + "cursor": {}, + }, + ) + assertSuccess( + result, + [{"result": {}}], + msg="$$ROOT should return an empty object when the input document has no fields", + ) # Property [Reported Type]: $$ROOT always reports BSON type "object".