-
Notifications
You must be signed in to change notification settings - Fork 7
Added tests for $expr #127
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| """ | ||
| Tests for $expr argument shapes and expression evaluation. | ||
|
|
||
| Covers valid argument forms (field references, comparison expressions, | ||
| deeply nested operators), system variables ($$ROOT, $$CURRENT, $literal), | ||
| computed result truthiness, and constant expression evaluation. | ||
| """ | ||
|
|
||
| import pytest | ||
|
|
||
| from documentdb_tests.compatibility.tests.core.operator.query.utils.query_test_case import ( | ||
| QueryTestCase, | ||
| ) | ||
| from documentdb_tests.framework.assertions import assertResult | ||
| from documentdb_tests.framework.executor import execute_command | ||
| from documentdb_tests.framework.parametrize import pytest_params | ||
|
|
||
| ALL_TESTS: list[QueryTestCase] = [ | ||
| QueryTestCase( | ||
| id="field_ref_truthy", | ||
| doc=[{"_id": 1, "a": 5}], | ||
| filter={"$expr": "$a"}, | ||
| expected=[{"_id": 1, "a": 5}], | ||
| msg="non-zero field value is truthy", | ||
| ), | ||
| QueryTestCase( | ||
| id="field_ref_falsy", | ||
| doc=[{"_id": 1, "a": 0}], | ||
| filter={"$expr": "$a"}, | ||
| expected=[], | ||
| msg="zero field value is falsy", | ||
| ), | ||
| QueryTestCase( | ||
| id="field_ref_missing", | ||
| doc=[{"_id": 1, "b": 1}], | ||
| filter={"$expr": "$a"}, | ||
| expected=[], | ||
| msg="missing field is falsy", | ||
| ), | ||
| QueryTestCase( | ||
| id="comparison_two_fields", | ||
| doc=[{"_id": 1, "a": 5, "b": 3}, {"_id": 2, "a": 1, "b": 3}], | ||
| filter={"$expr": {"$gt": ["$a", "$b"]}}, | ||
| expected=[{"_id": 1, "a": 5, "b": 3}], | ||
| msg="$expr with comparison expression", | ||
| ), | ||
| QueryTestCase( | ||
| id="deeply_nested_operators", | ||
| doc=[{"_id": 1, "a": 4.5}, {"_id": 2, "a": 1.2}], | ||
| filter={"$expr": {"$gt": [{"$add": [1, {"$abs": {"$ceil": "$a"}}]}, 5]}}, | ||
| expected=[{"_id": 1, "a": 4.5}], | ||
| msg="$expr with deeply nested expression operators", | ||
| ), | ||
| QueryTestCase( | ||
| id="system_var_root", | ||
| doc=[{"_id": 1, "a": 1}], | ||
| filter={"$expr": {"$eq": [{"$type": "$$ROOT"}, "object"]}}, | ||
| expected=[{"_id": 1, "a": 1}], | ||
| msg="$expr with $$ROOT system variable", | ||
| ), | ||
| QueryTestCase( | ||
| id="system_var_current", | ||
| doc=[{"_id": 1, "a": 5}, {"_id": 2, "a": -1}], | ||
| filter={"$expr": {"$gt": ["$$CURRENT.a", 0]}}, | ||
| expected=[{"_id": 1, "a": 5}], | ||
| msg="$expr with $$CURRENT system variable", | ||
| ), | ||
| QueryTestCase( | ||
| id="literal_dollar_string", | ||
| doc=[{"_id": 1, "a": "$hello"}, {"_id": 2, "a": "world"}], | ||
| filter={"$expr": {"$eq": [{"$literal": "$hello"}, "$a"]}}, | ||
| expected=[{"_id": 1, "a": "$hello"}], | ||
| msg="$expr with $literal preserving dollar-prefixed string", | ||
| ), | ||
| QueryTestCase( | ||
| id="computed_zero_falsy", | ||
| doc=[{"_id": 1, "arr": [0]}], | ||
| filter={"$expr": {"$arrayElemAt": ["$arr", 0]}}, | ||
| expected=[], | ||
| msg="computed zero from $arrayElemAt is falsy", | ||
| ), | ||
| QueryTestCase( | ||
| id="computed_missing_falsy", | ||
| doc=[{"_id": 1, "arr": []}], | ||
| filter={"$expr": {"$arrayElemAt": ["$arr", 0]}}, | ||
| expected=[], | ||
| msg="$arrayElemAt on empty array returns missing (falsy)", | ||
| ), | ||
| QueryTestCase( | ||
| id="constant_eq_true", | ||
| doc=[{"_id": 1, "a": 1}], | ||
| filter={"$expr": {"$eq": [1, 1]}}, | ||
| expected=[{"_id": 1, "a": 1}], | ||
| msg="$expr evaluates constant expression — always true", | ||
| ), | ||
| QueryTestCase( | ||
| id="constant_add_truthy", | ||
| doc=[{"_id": 1, "a": 1}], | ||
| filter={"$expr": {"$add": [1, 2]}}, | ||
| expected=[{"_id": 1, "a": 1}], | ||
| msg="$expr truthiness on computed non-zero result", | ||
| ), | ||
| ] | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("test", pytest_params(ALL_TESTS)) | ||
| def test_expr_argument_handling(collection, test): | ||
| """Test $expr argument shapes and expression evaluation.""" | ||
| collection.insert_many(test.doc) | ||
| result = execute_command(collection, {"find": collection.name, "filter": test.filter}) | ||
| assertResult(result, expected=test.expected, error_code=test.error_code, msg=test.msg) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| """ | ||
| Tests for $expr edge cases and unusual expression forms. | ||
|
|
||
| Covers empty object/array as $expr argument, $or mixing regular query | ||
| and $expr, field path through array of objects, and $$REMOVE behavior. | ||
| """ | ||
|
|
||
| import pytest | ||
|
|
||
| from documentdb_tests.compatibility.tests.core.operator.query.utils.query_test_case import ( | ||
| QueryTestCase, | ||
| ) | ||
| from documentdb_tests.framework.assertions import assertResult | ||
| from documentdb_tests.framework.executor import execute_command | ||
| from documentdb_tests.framework.parametrize import pytest_params | ||
|
|
||
| ALL_TESTS: list[QueryTestCase] = [ | ||
| QueryTestCase( | ||
| id="expr_empty_object", | ||
| doc=[{"_id": 1}], | ||
| filter={"$expr": {}}, | ||
| expected=[{"_id": 1}], | ||
| msg="$expr with empty object {} — truthy, matches all", | ||
| ), | ||
| QueryTestCase( | ||
| id="expr_empty_array", | ||
| doc=[{"_id": 1}], | ||
| filter={"$expr": []}, | ||
| expected=[{"_id": 1}], | ||
| msg="$expr with empty array [] — truthy, matches all", | ||
| ), | ||
| QueryTestCase( | ||
| id="or_regular_and_expr", | ||
| doc=[ | ||
| {"_id": 1, "a": 1, "b": 5, "c": 3}, | ||
| {"_id": 2, "a": 2, "b": 1, "c": 5}, | ||
| {"_id": 3, "a": 3, "b": 1, "c": 1}, | ||
| ], | ||
| filter={"$or": [{"a": 1}, {"$expr": {"$gt": ["$b", "$c"]}}]}, | ||
| expected=[{"_id": 1, "a": 1, "b": 5, "c": 3}], | ||
| msg="$or mixing regular query and $expr", | ||
| ), | ||
| QueryTestCase( | ||
| id="field_path_through_array", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a case with |
||
| doc=[{"_id": 1, "items": [{"price": 50}, {"price": 150}]}], | ||
| filter={"$expr": {"$in": [150, "$items.price"]}}, | ||
| expected=[{"_id": 1, "items": [{"price": 50}, {"price": 150}]}], | ||
| msg="$expr with field path through array of objects", | ||
| ), | ||
| QueryTestCase( | ||
| id="cond_with_remove", | ||
| doc=[{"_id": 1, "a": 5}, {"_id": 2, "a": -1}], | ||
| filter={"$expr": {"$cond": [{"$gt": ["$a", 0]}, "$a", "$$REMOVE"]}}, | ||
| expected=[{"_id": 1, "a": 5}], | ||
| msg="$expr with $$REMOVE in $cond false branch — falsy", | ||
| ), | ||
| QueryTestCase( | ||
| id="bare_remove_falsy", | ||
| doc=[{"_id": 1}], | ||
| filter={"$expr": "$$REMOVE"}, | ||
| expected=[], | ||
| msg="$expr with bare $$REMOVE — falsy, no documents match", | ||
| ), | ||
| ] | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing: |
||
|
|
||
| @pytest.mark.parametrize("test", pytest_params(ALL_TESTS)) | ||
| def test_expr_complex(collection, test): | ||
| """Test $expr edge cases and unusual expressions.""" | ||
| collection.insert_many(test.doc) | ||
| result = execute_command(collection, {"find": collection.name, "filter": test.filter}) | ||
| assertResult(result, expected=test.expected, error_code=test.error_code, msg=test.msg) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add $$NOW since its also system var?