From 0bc0149d2e8075dbadbbee0ec1c56b086057d823 Mon Sep 17 00:00:00 2001 From: PatersonProjects Date: Tue, 16 Jun 2026 15:36:17 -0700 Subject: [PATCH 1/6] Add ping Tests Signed-off-by: PatersonProjects --- .../diagnostic/commands/ping/__init__.py | 0 .../ping/test_ping_argument_handling.py | 71 +++++++++++++++++++ .../commands/ping/test_ping_core_behavior.py | 64 +++++++++++++++++ .../ping/test_ping_error_conditions.py | 42 +++++++++++ .../ping/test_ping_response_structure.py | 37 ++++++++++ 5 files changed, 214 insertions(+) create mode 100644 documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/__init__.py create mode 100644 documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_argument_handling.py create mode 100644 documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_core_behavior.py create mode 100644 documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_error_conditions.py create mode 100644 documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_response_structure.py diff --git a/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/__init__.py b/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_argument_handling.py b/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_argument_handling.py new file mode 100644 index 000000000..f44608554 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_argument_handling.py @@ -0,0 +1,71 @@ +"""Tests for ping command argument handling. + +Validates that ping accepts any BSON type as its command value, as well as +numeric edge cases (negative int, zero, infinity). The command value does +not affect behavior — all inputs should return ok: 1. +""" + +import pytest + +from documentdb_tests.compatibility.tests.system.diagnostic.utils.diagnostic_test_case import ( + DiagnosticTestCase, +) +from documentdb_tests.framework.assertions import assertProperties +from documentdb_tests.framework.bson_type_validator import ( + BsonType, + BsonTypeTestCase, + generate_bson_acceptance_test_cases, +) +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 +from documentdb_tests.framework.test_constants import FLOAT_INFINITY + +pytestmark = pytest.mark.admin + + +PING_BSON_TYPE_SPECS = [ + BsonTypeTestCase( + id="ping_value", + msg="ping command should accept any BSON type as value", + valid_types=list(BsonType), + ), +] + +ACCEPTANCE_CASES = generate_bson_acceptance_test_cases(PING_BSON_TYPE_SPECS) + + +@pytest.mark.parametrize("bson_type,sample_value,spec", ACCEPTANCE_CASES) +def test_ping_argument_types(collection, bson_type, sample_value, spec): + """Test that ping accepts various BSON types as command value.""" + result = execute_admin_command(collection, {"ping": sample_value}) + assertProperties(result, {"ok": Eq(1.0)}, msg=spec.msg, raw_res=True) + + +NUMERIC_EDGE_CASES: list[DiagnosticTestCase] = [ + DiagnosticTestCase( + id="negative_int", + command={"ping": -1}, + checks={"ok": Eq(1.0)}, + msg="Should accept negative int", + ), + DiagnosticTestCase( + id="int_0", + command={"ping": 0}, + checks={"ok": Eq(1.0)}, + msg="Should accept int 0", + ), + DiagnosticTestCase( + id="infinity", + command={"ping": FLOAT_INFINITY}, + checks={"ok": Eq(1.0)}, + msg="Should accept infinity", + ), +] + + +@pytest.mark.parametrize("test", pytest_params(NUMERIC_EDGE_CASES)) +def test_ping_argument_numeric_edge_cases(collection, test): + """Test that ping accepts edge-case numeric values as command value.""" + 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/diagnostic/commands/ping/test_ping_core_behavior.py b/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_core_behavior.py new file mode 100644 index 000000000..56d39e3cf --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_core_behavior.py @@ -0,0 +1,64 @@ +"""Tests for ping command core behavior. + +Validates that ping succeeds on both admin and non-admin databases, +can be executed repeatedly in succession, and works after other commands +or write activity. +""" + +import pytest + +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_admin_command, execute_command +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.property_checks import Eq + +pytestmark = pytest.mark.admin + + +DATABASE_TESTS: list[DiagnosticTestCase] = [ + DiagnosticTestCase( + id="admin_database", + command={"ping": 1}, + use_admin=True, + checks={"ok": Eq(1.0)}, + msg="Should succeed on admin database", + ), + DiagnosticTestCase( + id="non_admin_database", + command={"ping": 1}, + use_admin=False, + checks={"ok": Eq(1.0)}, + msg="Should succeed on non-admin database", + ), +] + + +@pytest.mark.parametrize("test", pytest_params(DATABASE_TESTS)) +def test_ping_on_database(collection, test): + """Test ping returns ok:1 on both admin and non-admin databases.""" + if test.use_admin: + result = execute_admin_command(collection, test.command) + else: + result = execute_command(collection, test.command) + assertProperties(result, test.checks, msg=test.msg, raw_res=True) + + +def test_ping_multiple_times_in_succession(collection): + """Test ping can be executed multiple times in succession.""" + for _ in range(3): + result = execute_admin_command(collection, {"ping": 1}) + assertProperties( + result, {"ok": Eq(1.0)}, msg="Should succeed on repeated execution", raw_res=True + ) + + +def test_ping_after_write_activity(collection): + """Test ping returns ok:1 immediately after write activity.""" + collection.insert_many([{"_id": i, "data": "x" * 100} for i in range(100)]) + result = execute_admin_command(collection, {"ping": 1}) + assertProperties( + result, {"ok": Eq(1.0)}, msg="Should succeed after write activity", raw_res=True + ) diff --git a/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_error_conditions.py b/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_error_conditions.py new file mode 100644 index 000000000..0f6711077 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_error_conditions.py @@ -0,0 +1,42 @@ +"""Tests for ping command error conditions. + +Validates that invalid usages of ping produce appropriate errors. +""" + +import pytest + +from documentdb_tests.compatibility.tests.system.diagnostic.utils.diagnostic_test_case import ( + DiagnosticTestCase, +) +from documentdb_tests.framework.assertions import assertFailureCode +from documentdb_tests.framework.error_codes import ( + COMMAND_NOT_FOUND_ERROR, + UNRECOGNIZED_COMMAND_FIELD_ERROR, +) +from documentdb_tests.framework.executor import execute_admin_command +from documentdb_tests.framework.parametrize import pytest_params + +pytestmark = pytest.mark.admin + + +ERROR_TESTS: list[DiagnosticTestCase] = [ + DiagnosticTestCase( + id="unrecognized_field", + command={"ping": 1, "unknownField": "test"}, + error_code=UNRECOGNIZED_COMMAND_FIELD_ERROR, + msg="Should reject unrecognized fields", + ), + DiagnosticTestCase( + id="case_sensitive", + command={"Ping": 1}, + error_code=COMMAND_NOT_FOUND_ERROR, + msg="Case-mismatched command name should fail", + ), +] + + +@pytest.mark.parametrize("test", pytest_params(ERROR_TESTS)) +def test_ping_error_conditions(collection, test): + """Verifies ping rejects invalid usages with appropriate error codes.""" + result = execute_admin_command(collection, test.command) + assertFailureCode(result, test.error_code, msg=test.msg) diff --git a/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_response_structure.py b/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_response_structure.py new file mode 100644 index 000000000..4c8418369 --- /dev/null +++ b/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_response_structure.py @@ -0,0 +1,37 @@ +"""Tests for ping command response structure. + +Validates the response fields and their types. +""" + +import pytest + +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_admin_command +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.property_checks import Eq, IsType + +pytestmark = pytest.mark.admin + + +RESPONSE_TESTS: list[DiagnosticTestCase] = [ + DiagnosticTestCase( + id="ok_field_value", + checks={"ok": Eq(1.0)}, + msg="'ok' field should be 1.0", + ), + DiagnosticTestCase( + id="ok_field_type", + checks={"ok": IsType("double")}, + msg="'ok' field should be a double", + ), +] + + +@pytest.mark.parametrize("test", pytest_params(RESPONSE_TESTS)) +def test_ping_response_properties(collection, test): + """Verifies ping response fields have expected types and values.""" + result = execute_admin_command(collection, {"ping": 1}) + assertProperties(result, test.checks, msg=test.msg, raw_res=True) From 51a482ab4db4faa743125c3ac8699d126a8fbf33 Mon Sep 17 00:00:00 2001 From: PatersonProjects Date: Tue, 30 Jun 2026 13:32:35 -0700 Subject: [PATCH 2/6] Added tests Signed-off-by: PatersonProjects --- .../arithmetic/add/test_add_date.py | 162 +++++++++++ .../arithmetic/add/test_add_errors.py | 203 ++++++++++++++ .../arithmetic/add/test_add_input_forms.py | 47 ++++ .../arithmetic/add/test_add_non_finite.py | 154 +++++++++++ .../arithmetic/add/test_add_null.py | 86 ++++++ .../arithmetic/add/test_add_numeric.py | 259 ++++++++++++++++++ .../arithmetic/add/test_add_overflow.py | 95 +++++++ .../arithmetic/add/test_add_precision.py | 103 +++++++ .../arithmetic/add/test_add_return_type.py | 114 ++++++++ 9 files changed, 1223 insertions(+) create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py new file mode 100644 index 000000000..7cd0f09f2 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py @@ -0,0 +1,162 @@ +from datetime import datetime, timedelta, timezone + +import pytest +from bson import Decimal128, Int64 + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 + ExpressionTestCase, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params + +# Property [Date Arithmetic]: $add accepts exactly one date operand and one or more numeric +# operands (in milliseconds). The date may appear in any position. +ADD_DATE_NUMERIC_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "date_int32", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 86400000}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 2, tzinfo=timezone.utc), + msg="$add should add int32 milliseconds to a date", + ), + ExpressionTestCase( + "date_int64", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": Int64(86400000)}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 2, tzinfo=timezone.utc), + msg="$add should add int64 milliseconds to a date", + ), + ExpressionTestCase( + "date_decimal", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": Decimal128("1.5")}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 1, 0, 0, 0, 2000, tzinfo=timezone.utc), + msg="$add should round a decimal128 fractional millisecond value when adding to a date", + ), + ExpressionTestCase( + "date_double_round_up", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 2.5}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 1, 0, 0, 0, 3000, tzinfo=timezone.utc), + msg="$add should round up a double fractional millisecond value (.5) when adding to a date", + ), + ExpressionTestCase( + "date_double_truncates", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 4.4}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 1, 0, 0, 0, 4000, tzinfo=timezone.utc), + msg="$add should truncate a double fractional millisecond value (<.5) when adding to a date", # noqa: E501 + ), +] + +# Property [Date Rounding Boundaries]: $add rounds fractional millisecond offsets using +# round-half-away-from-zero. Values with |frac| < 0.5 truncate toward zero; values with +# |frac| >= 0.5 round away from zero. +ADD_DATE_ROUNDING_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "date_double_0_1", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 0.1}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 1, tzinfo=timezone.utc), + msg="$add should truncate 0.1ms and leave the date unchanged", + ), + ExpressionTestCase( + "date_double_0_49", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 0.49}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 1, tzinfo=timezone.utc), + msg="$add should truncate 0.49ms and leave the date unchanged", + ), + ExpressionTestCase( + "date_double_0_51", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 0.51}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 1, 0, 0, 0, 1000, tzinfo=timezone.utc), + msg="$add should round up 0.51ms to 1ms when adding to a date", + ), + ExpressionTestCase( + "date_double_0_6", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 0.6}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 1, 0, 0, 0, 1000, tzinfo=timezone.utc), + msg="$add should round up 0.6ms to 1ms when adding to a date", + ), + ExpressionTestCase( + "date_double_neg_0_5", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": -0.5}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 1, tzinfo=timezone.utc) - timedelta(milliseconds=1), + msg="$add should round -0.5ms away from zero to -1ms when adding to a date", + ), + ExpressionTestCase( + "date_double_neg_0_51", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": -0.51}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 1, tzinfo=timezone.utc) - timedelta(milliseconds=1), + msg="$add should round -0.51ms away from zero to -1ms when adding to a date", + ), +] + +# Property [Date Operand Position]: the date operand may appear in any position among the +# operands; only one date is permitted. +ADD_DATE_POSITION_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "number_then_date", + doc={"a": 86400000, "b": datetime(2026, 1, 1, tzinfo=timezone.utc)}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 2, tzinfo=timezone.utc), + msg="$add should add a date when the numeric operand appears before the date", + ), + ExpressionTestCase( + "date_in_middle", + doc={"a": 1, "b": datetime(2026, 1, 1, tzinfo=timezone.utc), "c": 1000}, + expression={"$add": ["$a", "$b", "$c"]}, + expected=datetime(2026, 1, 1, 0, 0, 1, 1000, tzinfo=timezone.utc), + msg="$add should add a date when it appears in the middle of the operand list", + ), +] + +# Property [Date Sign Handling]: adding zero or a negative number of milliseconds to a date +# returns the date unchanged or subtracted. +ADD_DATE_SIGN_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "date_negative", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": -86400000}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2025, 12, 31, tzinfo=timezone.utc), + msg="$add should subtract milliseconds from a date when adding a negative number", + ), + ExpressionTestCase( + "date_zero", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 0}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 1, tzinfo=timezone.utc), + msg="$add should return the same date when adding zero milliseconds", + ), + ExpressionTestCase( + "date_negative_zero", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": -0.0}, + expression={"$add": ["$a", "$b"]}, + expected=datetime(2026, 1, 1, tzinfo=timezone.utc), + msg="$add should return the same date when adding negative zero", + ), +] + +ADD_DATE_ALL_TESTS = ( + ADD_DATE_NUMERIC_TESTS + ADD_DATE_POSITION_TESTS + ADD_DATE_SIGN_TESTS + ADD_DATE_ROUNDING_TESTS +) + + +@pytest.mark.parametrize("test_case", pytest_params(ADD_DATE_ALL_TESTS)) +def test_add_date(collection, test_case: ExpressionTestCase): + """Test $add date arithmetic: numeric types, operand position, and sign handling.""" + result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py new file mode 100644 index 000000000..f5c817b3d --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py @@ -0,0 +1,203 @@ +from datetime import datetime, timezone + +import pytest +from bson import Binary, Code, MaxKey, MinKey, ObjectId, Regex, Timestamp + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 + ExpressionTestCase, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression_with_insert, +) +from documentdb_tests.framework.error_codes import ( + MORE_THAN_ONE_DATE_ERROR, + OVERFLOW_ERROR, + TYPE_MISMATCH_ERROR, +) +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_constants import ( + DECIMAL128_INFINITY, + DECIMAL128_NAN, + FLOAT_INFINITY, + FLOAT_NAN, + INT64_MAX, +) + +# Property [Type Strictness]: $add rejects non-numeric, non-date operand types. +ADD_TYPE_ERROR_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + f"type_{tid}", + doc={"a": 1, "b": val}, + expression={"$add": ["$a", "$b"]}, + error_code=TYPE_MISMATCH_ERROR, + msg=f"$add should reject a {tid} operand", + ) + for tid, val in [ + ("string", "string"), + ("bool", True), + ("array", [2, 3]), + ("object", {"a": 2}), + ("regex", Regex("abc")), + ("objectid", ObjectId("507f1f77bcf86cd799439011")), + ("binary", Binary(b"data")), + ("minkey", MinKey()), + ("maxkey", MaxKey()), + ("timestamp", Timestamp(1, 1)), + ("code", Code("function(){}")), + ] +] + +# Property [Mixed Valid and Invalid]: $add rejects an invalid operand when it appears among +# valid numeric operands. +ADD_MIXED_VALID_INVALID_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "mixed_valid_invalid", + doc={"a": 1, "b": 2, "c": "string"}, + expression={"$add": ["$a", "$b", "$c"]}, + error_code=TYPE_MISMATCH_ERROR, + msg="$add should error when a string appears among numeric operands", + ), +] + +# Property [Single Invalid Operand]: $add rejects a single operand of an invalid type. +ADD_SINGLE_TYPE_ERROR_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "single_string", + doc={"a": "string"}, + expression={"$add": ["$a"]}, + error_code=TYPE_MISMATCH_ERROR, + msg="$add should reject a single string operand", + ), + ExpressionTestCase( + "single_boolean", + doc={"a": True}, + expression={"$add": ["$a"]}, + error_code=TYPE_MISMATCH_ERROR, + msg="$add should reject a single boolean operand", + ), + ExpressionTestCase( + "single_array", + doc={"a": [1, 2]}, + expression={"$add": ["$a"]}, + error_code=TYPE_MISMATCH_ERROR, + msg="$add should reject a single array operand", + ), + ExpressionTestCase( + "single_object", + doc={"a": {"x": 1}}, + expression={"$add": ["$a"]}, + error_code=TYPE_MISMATCH_ERROR, + msg="$add should reject a single object operand", + ), +] + +# Property [Multiple Dates]: $add rejects expressions with more than one date operand. +ADD_MULTIPLE_DATE_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "add_two_identical_dates", + doc={ + "a": datetime(2026, 1, 1, tzinfo=timezone.utc), + "b": datetime(2026, 1, 1, tzinfo=timezone.utc), + }, + expression={"$add": ["$a", "$b"]}, + error_code=MORE_THAN_ONE_DATE_ERROR, + msg="$add should error when adding two identical date operands", + ), + ExpressionTestCase( + "two_different_dates", + doc={ + "a": datetime(2026, 1, 1, tzinfo=timezone.utc), + "b": datetime(2026, 1, 2, tzinfo=timezone.utc), + }, + expression={"$add": ["$a", "$b"]}, + error_code=MORE_THAN_ONE_DATE_ERROR, + msg="$add should error when adding two different date operands", + ), + ExpressionTestCase( + "two_dates_with_numbers", + doc={ + "a": datetime(2026, 1, 1, tzinfo=timezone.utc), + "b": datetime(2026, 1, 2, tzinfo=timezone.utc), + }, + expression={"$add": [1, 2, 3, "$a", "$b"]}, + error_code=MORE_THAN_ONE_DATE_ERROR, + msg="$add should error when two dates appear among numeric operands", + ), + ExpressionTestCase( + "dates_separated_by_number", + doc={ + "a": datetime(2026, 1, 1, tzinfo=timezone.utc), + "b": datetime(2026, 1, 2, tzinfo=timezone.utc), + }, + expression={"$add": ["$a", 1, "$b"]}, + error_code=MORE_THAN_ONE_DATE_ERROR, + msg="$add should error when two dates are separated by a numeric operand", + ), +] + +# Property [Date with Non-Finite]: $add rejects NaN and Infinity as numeric operands when a +# date is also present, since the resulting date would be non-representable. +ADD_DATE_NON_FINITE_ERROR_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "date_nan", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": FLOAT_NAN}, + expression={"$add": ["$a", "$b"]}, + error_code=OVERFLOW_ERROR, + msg="$add should error when adding a date and float NaN", + ), + ExpressionTestCase( + "date_infinity", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": FLOAT_INFINITY}, + expression={"$add": ["$a", "$b"]}, + error_code=OVERFLOW_ERROR, + msg="$add should error when adding a date and float infinity", + ), + ExpressionTestCase( + "date_decimal_nan", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": DECIMAL128_NAN}, + expression={"$add": ["$a", "$b"]}, + error_code=OVERFLOW_ERROR, + msg="$add should error when adding a date and decimal128 NaN", + ), + ExpressionTestCase( + "date_decimal_infinity", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": DECIMAL128_INFINITY}, + expression={"$add": ["$a", "$b"]}, + error_code=OVERFLOW_ERROR, + msg="$add should error when adding a date and decimal128 infinity", + ), +] + +# Property [Date Overflow]: $add errors when the millisecond offset would push the date result +# beyond the representable date range. +ADD_DATE_OVERFLOW_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "date_int64_max", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": INT64_MAX}, + expression={"$add": ["$a", "$b"]}, + error_code=OVERFLOW_ERROR, + msg="$add should error when adding INT64_MAX milliseconds to a date overflows the date range", # noqa: E501 + ), +] + +ADD_ERROR_ALL_TESTS = ( + ADD_TYPE_ERROR_TESTS + + ADD_MIXED_VALID_INVALID_TESTS + + ADD_SINGLE_TYPE_ERROR_TESTS + + ADD_MULTIPLE_DATE_TESTS + + ADD_DATE_NON_FINITE_ERROR_TESTS + + ADD_DATE_OVERFLOW_TESTS +) + + +@pytest.mark.parametrize("test_case", pytest_params(ADD_ERROR_ALL_TESTS)) +def test_add_errors(collection, test_case: ExpressionTestCase): + """Test $add type, multiple-date, and date non-finite error cases.""" + result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py new file mode 100644 index 000000000..7019c1b0b --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py @@ -0,0 +1,47 @@ +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 + ExpressionTestCase, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params + +# Property [Expression Input]: $add evaluates a nested expression argument before summing. +ADD_EXPRESSION_INPUT_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "nested_add", + doc={"a": 1, "b": 2}, + expression={"$add": [{"$add": ["$a", "$b"]}, 3]}, + expected=6, + msg="$add should evaluate a nested $add expression as an operand", + ), +] + +# Property [Mixed Literal and Field]: $add accepts a mix of field references and inline literals +# in the same operand list. +ADD_MIXED_INPUT_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "mixed_literal_and_field", + doc={"a": 10}, + expression={"$add": ["$a", 5]}, + expected=15, + msg="$add should sum a field reference and an inline literal operand", + ), +] + +ADD_INPUT_FORM_ALL_TESTS = ADD_EXPRESSION_INPUT_TESTS + ADD_MIXED_INPUT_TESTS + + +@pytest.mark.parametrize("test_case", pytest_params(ADD_INPUT_FORM_ALL_TESTS)) +def test_add_input_forms(collection, test_case: ExpressionTestCase): + """Test $add literal, nested expression, and mixed input form cases.""" + result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py new file mode 100644 index 000000000..0343c3316 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py @@ -0,0 +1,154 @@ +import math + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 + ExpressionTestCase, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_constants import ( + DECIMAL128_INFINITY, + DECIMAL128_NAN, + DECIMAL128_NEGATIVE_INFINITY, + FLOAT_INFINITY, + FLOAT_NAN, + FLOAT_NEGATIVE_INFINITY, +) + +# Property [Infinity]: $add propagates infinity according to IEEE 754 rules. +ADD_INFINITY_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "infinity", + doc={"a": FLOAT_INFINITY, "b": 1}, + expression={"$add": ["$a", "$b"]}, + expected=FLOAT_INFINITY, + msg="$add should return infinity when adding infinity and a finite number", + ), + ExpressionTestCase( + "negative_infinity", + doc={"a": FLOAT_NEGATIVE_INFINITY, "b": 1}, + expression={"$add": ["$a", "$b"]}, + expected=FLOAT_NEGATIVE_INFINITY, + msg="$add should return negative infinity when adding negative infinity and a finite number", # noqa: E501 + ), + ExpressionTestCase( + "single_infinity", + doc={"a": FLOAT_INFINITY}, + expression={"$add": ["$a"]}, + expected=FLOAT_INFINITY, + msg="$add should return infinity for a single infinity operand", + ), + ExpressionTestCase( + "inf_plus_inf", + doc={"a": FLOAT_INFINITY, "b": FLOAT_INFINITY}, + expression={"$add": ["$a", "$b"]}, + expected=FLOAT_INFINITY, + msg="$add should return infinity when adding two positive infinities", + ), + ExpressionTestCase( + "neg_inf_plus_neg_inf", + doc={"a": FLOAT_NEGATIVE_INFINITY, "b": FLOAT_NEGATIVE_INFINITY}, + expression={"$add": ["$a", "$b"]}, + expected=FLOAT_NEGATIVE_INFINITY, + msg="$add should return negative infinity when adding two negative infinities", + ), + ExpressionTestCase( + "inf_plus_zero", + doc={"a": FLOAT_INFINITY, "b": 0}, + expression={"$add": ["$a", "$b"]}, + expected=FLOAT_INFINITY, + msg="$add should return infinity when adding infinity and zero", + ), + ExpressionTestCase( + "neg_inf_plus_zero", + doc={"a": FLOAT_NEGATIVE_INFINITY, "b": 0}, + expression={"$add": ["$a", "$b"]}, + expected=FLOAT_NEGATIVE_INFINITY, + msg="$add should return negative infinity when adding negative infinity and zero", + ), + ExpressionTestCase( + "decimal_infinity", + doc={"a": DECIMAL128_INFINITY, "b": 1}, + expression={"$add": ["$a", "$b"]}, + expected=DECIMAL128_INFINITY, + msg="$add should return decimal128 infinity when adding decimal128 infinity and a number", + ), + ExpressionTestCase( + "decimal_negative_infinity", + doc={"a": DECIMAL128_NEGATIVE_INFINITY, "b": 1}, + expression={"$add": ["$a", "$b"]}, + expected=DECIMAL128_NEGATIVE_INFINITY, + msg="$add should return decimal128 negative infinity when adding decimal128 negative infinity and a number", # noqa: E501 + ), +] + +# Property [NaN]: $add propagates NaN according to IEEE 754 rules. +ADD_NAN_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "nan_add_one", + doc={"a": FLOAT_NAN, "b": 1}, + expression={"$add": ["$a", "$b"]}, + expected=pytest.approx(math.nan, nan_ok=True), + msg="$add should return NaN when adding float NaN and a finite number", + ), + ExpressionTestCase( + "inf_minus_inf", + doc={"a": FLOAT_INFINITY, "b": FLOAT_NEGATIVE_INFINITY}, + expression={"$add": ["$a", "$b"]}, + expected=pytest.approx(math.nan, nan_ok=True), + msg="$add should return NaN when adding float infinity and negative infinity", + ), + ExpressionTestCase( + "nan_plus_nan", + doc={"a": FLOAT_NAN, "b": FLOAT_NAN}, + expression={"$add": ["$a", "$b"]}, + expected=pytest.approx(math.nan, nan_ok=True), + msg="$add should return NaN when adding two float NaN values", + ), + ExpressionTestCase( + "nan_plus_inf", + doc={"a": FLOAT_NAN, "b": FLOAT_INFINITY}, + expression={"$add": ["$a", "$b"]}, + expected=pytest.approx(math.nan, nan_ok=True), + msg="$add should return NaN when adding float NaN and infinity", + ), + ExpressionTestCase( + "decimal_nan", + doc={"a": DECIMAL128_NAN, "b": 1}, + expression={"$add": ["$a", "$b"]}, + expected=DECIMAL128_NAN, + msg="$add should return decimal128 NaN when adding decimal128 NaN and a number", + ), + ExpressionTestCase( + "decimal_nan_plus_nan", + doc={"a": DECIMAL128_NAN, "b": DECIMAL128_NAN}, + expression={"$add": ["$a", "$b"]}, + expected=DECIMAL128_NAN, + msg="$add should return decimal128 NaN when adding two decimal128 NaN values", + ), + ExpressionTestCase( + "decimal_inf_minus_inf", + doc={"a": DECIMAL128_INFINITY, "b": DECIMAL128_NEGATIVE_INFINITY}, + expression={"$add": ["$a", "$b"]}, + expected=DECIMAL128_NAN, + msg="$add should return decimal128 NaN when adding decimal128 infinity and negative infinity", # noqa: E501 + ), +] + +ADD_NON_FINITE_ALL_TESTS = ADD_INFINITY_TESTS + ADD_NAN_TESTS + + +@pytest.mark.parametrize("test_case", pytest_params(ADD_NON_FINITE_ALL_TESTS)) +def test_add_non_finite(collection, test_case: ExpressionTestCase): + """Test $add infinity and NaN propagation cases.""" + result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py new file mode 100644 index 000000000..9651d52d3 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py @@ -0,0 +1,86 @@ +from datetime import datetime, timezone + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 + ExpressionTestCase, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_constants import MISSING + +# Property [Null Propagation]: $add returns null if any operand is null or refers to a missing +# field. +ADD_NULL_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "single_null", + doc={"a": None}, + expression={"$add": ["$a"]}, + expected=None, + msg="$add should return null for a single null operand", + ), + ExpressionTestCase( + "null_operand", + doc={"a": 1, "b": None}, + expression={"$add": ["$a", "$b"]}, + expected=None, + msg="$add should return null when any operand is null", + ), + ExpressionTestCase( + "missing_field", + doc={}, + expression={"$add": [1, MISSING]}, + expected=None, + msg="$add should return null when any operand is a missing field", + ), + ExpressionTestCase( + "null_with_multiple", + doc={"a": 1, "b": 2, "c": None}, + expression={"$add": ["$a", "$b", "$c"]}, + expected=None, + msg="$add should return null when null appears among multiple operands", + ), + ExpressionTestCase( + "null_in_middle", + doc={"a": 1, "b": 2, "c": 3, "e": 5}, + expression={"$add": ["$a", "$b", "$c", None, "$e"]}, + expected=None, + msg="$add should return null when null appears in the middle of operands", + ), + ExpressionTestCase( + "all_null", + doc={"a": None, "b": None}, + expression={"$add": ["$a", "$b"]}, + expected=None, + msg="$add should return null when all operands are null", + ), + ExpressionTestCase( + "all_missing", + doc={}, + expression={"$add": [MISSING, MISSING]}, + expected=None, + msg="$add should return null when all operands are missing fields", + ), + ExpressionTestCase( + "date_and_null", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": None}, + expression={"$add": ["$a", "$b"]}, + expected=None, + msg="$add should return null when a date is paired with a null operand", + ), +] + + +@pytest.mark.parametrize("test_case", pytest_params(ADD_NULL_TESTS)) +def test_add_null(collection, test_case: ExpressionTestCase): + """Test $add null and missing field propagation cases.""" + result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py new file mode 100644 index 000000000..3c4d6f305 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py @@ -0,0 +1,259 @@ +import pytest +from bson import Decimal128, Int64 + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 + ExpressionTestCase, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params + +# Property [Same-Type Addition]: $add of two values of the same numeric type returns a value of +# that type. +ADD_SAME_TYPE_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "same_type_int32", + doc={"a": 1, "b": 2}, + expression={"$add": ["$a", "$b"]}, + expected=3, + msg="$add should add two int32 values", + ), + ExpressionTestCase( + "same_type_int64", + doc={"a": Int64(10), "b": Int64(20)}, + expression={"$add": ["$a", "$b"]}, + expected=Int64(30), + msg="$add should add two int64 values", + ), + ExpressionTestCase( + "same_type_double", + doc={"a": 1.5, "b": 2.5}, + expression={"$add": ["$a", "$b"]}, + expected=4.0, + msg="$add should add two double values", + ), + ExpressionTestCase( + "same_type_decimal", + doc={"a": Decimal128("10.5"), "b": Decimal128("20.5")}, + expression={"$add": ["$a", "$b"]}, + expected=Decimal128("31.0"), + msg="$add should add two decimal128 values", + ), +] + +# Property [Type Promotion]: $add promotes to the wider type when operands have different numeric +# types. Precedence: decimal128 > double > int64 > int32. +ADD_MIXED_TYPE_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "int32_int64", + doc={"a": 1, "b": Int64(20)}, + expression={"$add": ["$a", "$b"]}, + expected=Int64(21), + msg="$add should return int64 when adding int32 and int64", + ), + ExpressionTestCase( + "int32_double", + doc={"a": 1, "b": 2.5}, + expression={"$add": ["$a", "$b"]}, + expected=3.5, + msg="$add should return double when adding int32 and double", + ), + ExpressionTestCase( + "int32_decimal", + doc={"a": 1, "b": Decimal128("2.5")}, + expression={"$add": ["$a", "$b"]}, + expected=Decimal128("3.5"), + msg="$add should return decimal128 when adding int32 and decimal128", + ), + ExpressionTestCase( + "int64_double", + doc={"a": Int64(10), "b": 2.5}, + expression={"$add": ["$a", "$b"]}, + expected=12.5, + msg="$add should return double when adding int64 and double", + ), + ExpressionTestCase( + "int64_decimal", + doc={"a": Int64(10), "b": Decimal128("2.5")}, + expression={"$add": ["$a", "$b"]}, + expected=Decimal128("12.5"), + msg="$add should return decimal128 when adding int64 and decimal128", + ), + ExpressionTestCase( + "double_decimal", + doc={"a": 1.5, "b": Decimal128("2.5")}, + expression={"$add": ["$a", "$b"]}, + expected=pytest.approx(Decimal128("4.00000000000000")), + msg="$add should return decimal128 when adding double and decimal128", + ), + ExpressionTestCase( + "three_mixed_types", + doc={"a": Decimal128("1.5"), "b": 2.5, "c": Int64(3)}, + expression={"$add": ["$a", "$b", "$c"]}, + expected=pytest.approx(Decimal128("7.00000000000000")), + msg="$add should return decimal128 when adding decimal128, double, and int64", + ), +] + +# Property [Multiple Operands]: $add correctly sums three or more operands. +ADD_MULTIPLE_OPERANDS_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "multiple_int32", + doc={"a": 1, "b": 2, "c": 3}, + expression={"$add": ["$a", "$b", "$c"]}, + expected=6, + msg="$add should add multiple int32 values", + ), + ExpressionTestCase( + "multiple_int64", + doc={"a": Int64(1), "b": Int64(2), "c": Int64(3)}, + expression={"$add": ["$a", "$b", "$c"]}, + expected=Int64(6), + msg="$add should add multiple int64 values", + ), + ExpressionTestCase( + "multiple_double", + doc={"a": 1.1, "b": 2.2, "c": 3.3}, + expression={"$add": ["$a", "$b", "$c"]}, + expected=pytest.approx(6.6), + msg="$add should add multiple double values", + ), + ExpressionTestCase( + "multiple_decimal", + doc={ + "a": Decimal128("1"), + "b": Decimal128("2"), + "c": Decimal128("3"), + "d": Decimal128("4"), + }, + expression={"$add": ["$a", "$b", "$c", "$d"]}, + expected=Decimal128("10"), + msg="$add should add multiple decimal128 values", + ), + ExpressionTestCase( + "five_operands", + doc={"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}, + expression={"$add": ["$a", "$b", "$c", "$d", "$e"]}, + expected=15, + msg="$add should correctly sum five int32 operands", + ), + ExpressionTestCase( + "ten_operands", + doc={ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + "f": 6, + "g": 7, + "h": 8, + "i": 9, + "j": 10, + }, + expression={"$add": ["$a", "$b", "$c", "$d", "$e", "$f", "$g", "$h", "$i", "$j"]}, + expected=55, + msg="$add should correctly sum ten int32 operands", + ), +] + +# Property [Empty and Single Operand]: $add of zero operands returns 0; single operand returns +# that value unchanged. +ADD_SINGLE_AND_EMPTY_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "empty", + doc={}, + expression={"$add": []}, + expected=0, + msg="$add should return 0 for empty operand list", + ), + ExpressionTestCase( + "single_int32", + doc={"a": 5}, + expression={"$add": ["$a"]}, + expected=5, + msg="$add should return the value for a single int32 operand", + ), + ExpressionTestCase( + "single_int64", + doc={"a": Int64(0)}, + expression={"$add": ["$a"]}, + expected=Int64(0), + msg="$add should return the value for a single int64 operand", + ), + ExpressionTestCase( + "single_double", + doc={"a": 0.0}, + expression={"$add": ["$a"]}, + expected=0.0, + msg="$add should return the value for a single double operand", + ), + ExpressionTestCase( + "single_decimal", + doc={"a": Decimal128("0")}, + expression={"$add": ["$a"]}, + expected=Decimal128("0"), + msg="$add should return the value for a single decimal128 operand", + ), +] + +# Property [Sign Handling]: $add handles negative values and zero correctly. +ADD_SIGN_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "negative_positive", + doc={"a": -5, "b": 3}, + expression={"$add": ["$a", "$b"]}, + expected=-2, + msg="$add should add a negative and a positive int32 value", + ), + ExpressionTestCase( + "both_negative", + doc={"a": -10, "b": -20}, + expression={"$add": ["$a", "$b"]}, + expected=-30, + msg="$add should add two negative int32 values", + ), + ExpressionTestCase( + "zeros", + doc={"a": 0, "b": 0}, + expression={"$add": ["$a", "$b"]}, + expected=0, + msg="$add should return 0 when adding two int32 zeros", + ), + ExpressionTestCase( + "zero_negative_zero", + doc={"a": 0, "b": -0.0}, + expression={"$add": ["$a", "$b"]}, + expected=0.0, + msg="$add should return 0.0 when adding int32 zero and negative zero double", + ), + ExpressionTestCase( + "sum_to_zero", + doc={"a": 1, "b": 0, "c": -1}, + expression={"$add": ["$a", "$b", "$c"]}, + expected=0, + msg="$add should return 0 when operands sum to zero", + ), +] + +ADD_NUMERIC_ALL_TESTS = ( + ADD_SAME_TYPE_TESTS + + ADD_MIXED_TYPE_TESTS + + ADD_MULTIPLE_OPERANDS_TESTS + + ADD_SINGLE_AND_EMPTY_TESTS + + ADD_SIGN_TESTS +) + + +@pytest.mark.parametrize("test_case", pytest_params(ADD_NUMERIC_ALL_TESTS)) +def test_add_numeric(collection, test_case: ExpressionTestCase): + """Test $add numeric type combinations, multiple operands, and sign handling.""" + result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py new file mode 100644 index 000000000..29a992905 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py @@ -0,0 +1,95 @@ +import pytest +from bson import Int64 + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 + ExpressionTestCase, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_constants import ( + DOUBLE_FROM_INT64_MAX, + FLOAT_INFINITY, + FLOAT_NEGATIVE_INFINITY, + INT32_MAX, + INT32_MIN, + INT32_OVERFLOW, + INT32_UNDERFLOW, + INT64_MAX, + INT64_MIN, +) + +# Property [Int32 Overflow]: when an int32 result exceeds the int32 range, $add promotes to +# int64. +ADD_INT32_OVERFLOW_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "int32_overflow", + doc={"a": INT32_MAX, "b": 1}, + expression={"$add": ["$a", "$b"]}, + expected=Int64(INT32_OVERFLOW), + msg="$add should promote to int64 when the int32 result overflows INT32_MAX", + ), + ExpressionTestCase( + "int32_underflow", + doc={"a": INT32_MIN, "b": -1}, + expression={"$add": ["$a", "$b"]}, + expected=Int64(INT32_UNDERFLOW), + msg="$add should promote to int64 when the int32 result underflows INT32_MIN", + ), +] + +# Property [Int64 Overflow]: when an int64 result exceeds the int64 range, $add promotes to +# double. +ADD_INT64_OVERFLOW_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "int64_overflow", + doc={"a": INT64_MAX, "b": 1}, + expression={"$add": ["$a", "$b"]}, + expected=pytest.approx(DOUBLE_FROM_INT64_MAX), + msg="$add should promote to double when the int64 result overflows INT64_MAX", + ), + ExpressionTestCase( + "int64_underflow", + doc={"a": INT64_MIN, "b": -1}, + expression={"$add": ["$a", "$b"]}, + expected=pytest.approx(-DOUBLE_FROM_INT64_MAX), + msg="$add should promote to double when the int64 result underflows INT64_MIN", + ), +] + +# Property [Double Overflow]: when a double result exceeds the double range, $add returns +# infinity. +ADD_DOUBLE_OVERFLOW_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "double_overflow", + doc={"a": 1e308, "b": 1e308}, + expression={"$add": ["$a", "$b"]}, + expected=FLOAT_INFINITY, + msg="$add should return positive infinity on double overflow", + ), + ExpressionTestCase( + "double_underflow", + doc={"a": -1e308, "b": -1e308}, + expression={"$add": ["$a", "$b"]}, + expected=FLOAT_NEGATIVE_INFINITY, + msg="$add should return negative infinity on double underflow", + ), +] + +ADD_OVERFLOW_ALL_TESTS = ( + ADD_INT32_OVERFLOW_TESTS + ADD_INT64_OVERFLOW_TESTS + ADD_DOUBLE_OVERFLOW_TESTS +) + + +@pytest.mark.parametrize("test_case", pytest_params(ADD_OVERFLOW_ALL_TESTS)) +def test_add_overflow(collection, test_case: ExpressionTestCase): + """Test $add integer and double overflow and underflow cases.""" + result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py new file mode 100644 index 000000000..f0cdd34ab --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py @@ -0,0 +1,103 @@ +import pytest +from bson import Decimal128 + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 + ExpressionTestCase, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_constants import ( + DECIMAL128_INFINITY, + DECIMAL128_MAX, + DECIMAL128_MIN, + DECIMAL128_NEGATIVE_INFINITY, +) + +# Property [Decimal128 Precision]: $add preserves decimal128 precision, including exact +# representation of values that are inexact in double. +ADD_DECIMAL_PRECISION_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "decimal_precision", + doc={"a": Decimal128("1.5"), "b": Decimal128("2.5")}, + expression={"$add": ["$a", "$b"]}, + expected=Decimal128("4.0"), + msg="$add should preserve decimal128 precision", + ), + ExpressionTestCase( + "decimal_precision_small", + doc={"a": Decimal128("0.1"), "b": Decimal128("0.2")}, + expression={"$add": ["$a", "$b"]}, + expected=Decimal128("0.3"), + msg="$add should exactly represent 0.1 + 0.2 with decimal128", + ), + ExpressionTestCase( + "decimal_large_precision", + doc={ + "a": Decimal128("999999999999999999999999999999999"), + "b": Decimal128("1"), + }, + expression={"$add": ["$a", "$b"]}, + expected=Decimal128("1000000000000000000000000000000000"), + msg="$add should handle large decimal128 addition with full precision", + ), + ExpressionTestCase( + "decimal_large_negative_precision", + doc={ + "a": Decimal128("-999999999999999999999999999999999"), + "b": Decimal128("-1"), + }, + expression={"$add": ["$a", "$b"]}, + expected=Decimal128("-1000000000000000000000000000000000"), + msg="$add should handle large negative decimal128 addition with full precision", + ), +] + +# Property [Decimal128 Boundaries]: $add at decimal128 boundary values promotes to infinity when +# the result overflows, and returns zero when max and min cancel. +ADD_DECIMAL_BOUNDARY_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "decimal128_max_plus_zero", + doc={"a": DECIMAL128_MAX, "b": Decimal128("0")}, + expression={"$add": ["$a", "$b"]}, + expected=DECIMAL128_MAX, + msg="$add should return decimal128 max when adding zero to decimal128 max", + ), + ExpressionTestCase( + "decimal128_max_plus_max", + doc={"a": DECIMAL128_MAX, "b": DECIMAL128_MAX}, + expression={"$add": ["$a", "$b"]}, + expected=DECIMAL128_INFINITY, + msg="$add should return decimal128 infinity when adding two decimal128 max values", + ), + ExpressionTestCase( + "decimal128_min_plus_min", + doc={"a": DECIMAL128_MIN, "b": DECIMAL128_MIN}, + expression={"$add": ["$a", "$b"]}, + expected=DECIMAL128_NEGATIVE_INFINITY, + msg="$add should return decimal128 negative infinity when adding two decimal128 min values", + ), + ExpressionTestCase( + "decimal128_max_plus_min", + doc={"a": DECIMAL128_MAX, "b": DECIMAL128_MIN}, + expression={"$add": ["$a", "$b"]}, + expected=Decimal128("0E+6111"), + msg="$add should return zero when adding decimal128 max and decimal128 min", + ), +] + +ADD_PRECISION_ALL_TESTS = ADD_DECIMAL_PRECISION_TESTS + ADD_DECIMAL_BOUNDARY_TESTS + + +@pytest.mark.parametrize("test_case", pytest_params(ADD_PRECISION_ALL_TESTS)) +def test_add_precision(collection, test_case: ExpressionTestCase): + """Test $add decimal128 precision and boundary value cases.""" + result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py new file mode 100644 index 000000000..7af9e0b66 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py @@ -0,0 +1,114 @@ +from datetime import datetime, timezone + +import pytest +from bson import Decimal128, Int64 + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 + ExpressionTestCase, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params + +# Property [Return Type]: $add follows numeric type promotion rules to determine the result type. +# Precedence: decimal128 > double > int64 > int32. Date + numeric always returns date. +ADD_RETURN_TYPE_TESTS: list[ExpressionTestCase] = [ + ExpressionTestCase( + "return_type_int_int", + doc={"a": 1, "b": 2}, + expression={"$type": {"$add": ["$a", "$b"]}}, + expected="int", + msg="$add should return int type when adding two int32 values", + ), + ExpressionTestCase( + "return_type_int_long", + doc={"a": 1, "b": Int64(2)}, + expression={"$type": {"$add": ["$a", "$b"]}}, + expected="long", + msg="$add should return long type when adding int32 and int64", + ), + ExpressionTestCase( + "return_type_int_double", + doc={"a": 1, "b": 2.0}, + expression={"$type": {"$add": ["$a", "$b"]}}, + expected="double", + msg="$add should return double type when adding int32 and double", + ), + ExpressionTestCase( + "return_type_int_decimal", + doc={"a": 1, "b": Decimal128("2")}, + expression={"$type": {"$add": ["$a", "$b"]}}, + expected="decimal", + msg="$add should return decimal type when adding int32 and decimal128", + ), + ExpressionTestCase( + "return_type_long_long", + doc={"a": Int64(1), "b": Int64(2)}, + expression={"$type": {"$add": ["$a", "$b"]}}, + expected="long", + msg="$add should return long type when adding two int64 values", + ), + ExpressionTestCase( + "return_type_long_double", + doc={"a": Int64(1), "b": 2.0}, + expression={"$type": {"$add": ["$a", "$b"]}}, + expected="double", + msg="$add should return double type when adding int64 and double", + ), + ExpressionTestCase( + "return_type_long_decimal", + doc={"a": Int64(1), "b": Decimal128("2")}, + expression={"$type": {"$add": ["$a", "$b"]}}, + expected="decimal", + msg="$add should return decimal type when adding int64 and decimal128", + ), + ExpressionTestCase( + "return_type_double_double", + doc={"a": 1.0, "b": 2.0}, + expression={"$type": {"$add": ["$a", "$b"]}}, + expected="double", + msg="$add should return double type when adding two double values", + ), + ExpressionTestCase( + "return_type_double_decimal", + doc={"a": 1.0, "b": Decimal128("2")}, + expression={"$type": {"$add": ["$a", "$b"]}}, + expected="decimal", + msg="$add should return decimal type when adding double and decimal128", + ), + ExpressionTestCase( + "return_type_decimal_decimal", + doc={"a": Decimal128("1"), "b": Decimal128("2")}, + expression={"$type": {"$add": ["$a", "$b"]}}, + expected="decimal", + msg="$add should return decimal type when adding two decimal128 values", + ), + ExpressionTestCase( + "return_type_date_int", + doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 1000}, + expression={"$type": {"$add": ["$a", "$b"]}}, + expected="date", + msg="$add should return date type when adding a date and an int32", + ), + ExpressionTestCase( + "return_type_empty", + doc={}, + expression={"$type": {"$add": []}}, + expected="int", + msg="$add should return int type for an empty operand list", + ), +] + + +@pytest.mark.parametrize("test_case", pytest_params(ADD_RETURN_TYPE_TESTS)) +def test_add_return_type(collection, test_case: ExpressionTestCase): + """Test $add return type promotion rules for all numeric type combinations.""" + result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) From 014f2bceb8c06ba36a9e652a57ac7d81397bd67e Mon Sep 17 00:00:00 2001 From: PatersonProjects Date: Tue, 30 Jun 2026 13:40:50 -0700 Subject: [PATCH 3/6] Added docStrings to all files Signed-off-by: PatersonProjects --- .../core/operator/expressions/arithmetic/add/test_add_date.py | 4 ++++ .../operator/expressions/arithmetic/add/test_add_errors.py | 2 ++ .../expressions/arithmetic/add/test_add_input_forms.py | 2 ++ .../expressions/arithmetic/add/test_add_non_finite.py | 2 ++ .../core/operator/expressions/arithmetic/add/test_add_null.py | 2 ++ .../operator/expressions/arithmetic/add/test_add_numeric.py | 4 ++++ .../operator/expressions/arithmetic/add/test_add_overflow.py | 2 ++ .../operator/expressions/arithmetic/add/test_add_precision.py | 2 ++ .../expressions/arithmetic/add/test_add_return_type.py | 2 ++ 9 files changed, 22 insertions(+) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py index 7cd0f09f2..eff8f4af1 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py @@ -1,3 +1,7 @@ +"""Tests for $add date arithmetic including numeric offsets, rounding boundaries, operand +position, and sign handling. +""" + from datetime import datetime, timedelta, timezone import pytest diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py index f5c817b3d..127f0e5de 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py @@ -1,3 +1,5 @@ +"""Tests for $add error cases including invalid operand types, multiple dates, and date overflow.""" + from datetime import datetime, timezone import pytest diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py index 7019c1b0b..fa2f73df4 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py @@ -1,3 +1,5 @@ +"""Tests for $add input forms including nested expressions and mixed literal/field operands.""" + import pytest from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py index 0343c3316..958818e98 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py @@ -1,3 +1,5 @@ +"""Tests for $add infinity and NaN propagation.""" + import math import pytest diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py index 9651d52d3..3854d72d8 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py @@ -1,3 +1,5 @@ +"""Tests for $add null and missing field propagation.""" + from datetime import datetime, timezone import pytest diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py index 3c4d6f305..971b01581 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py @@ -1,3 +1,7 @@ +"""Tests for $add numeric operations including same-type and mixed-type addition, multiple +operands, empty/single operands, and sign handling. +""" + import pytest from bson import Decimal128, Int64 diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py index 29a992905..2da84306e 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py @@ -1,3 +1,5 @@ +"""Tests for $add integer and double overflow and underflow promotion.""" + import pytest from bson import Int64 diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py index f0cdd34ab..6658eb03a 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py @@ -1,3 +1,5 @@ +"""Tests for $add decimal128 precision and boundary value handling.""" + import pytest from bson import Decimal128 diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py index 7af9e0b66..d681c7fd2 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py @@ -1,3 +1,5 @@ +"""Tests for $add return type promotion rules across numeric and date operand combinations.""" + from datetime import datetime, timezone import pytest From 37407ae57c9912cf17ff31f5885fafea01f7db38 Mon Sep 17 00:00:00 2001 From: PatersonProjects Date: Tue, 30 Jun 2026 13:47:01 -0700 Subject: [PATCH 4/6] Revert "Added docStrings to all files" This reverts commit 014f2bceb8c06ba36a9e652a57ac7d81397bd67e. Signed-off-by: PatersonProjects --- .../core/operator/expressions/arithmetic/add/test_add_date.py | 4 ---- .../operator/expressions/arithmetic/add/test_add_errors.py | 2 -- .../expressions/arithmetic/add/test_add_input_forms.py | 2 -- .../expressions/arithmetic/add/test_add_non_finite.py | 2 -- .../core/operator/expressions/arithmetic/add/test_add_null.py | 2 -- .../operator/expressions/arithmetic/add/test_add_numeric.py | 4 ---- .../operator/expressions/arithmetic/add/test_add_overflow.py | 2 -- .../operator/expressions/arithmetic/add/test_add_precision.py | 2 -- .../expressions/arithmetic/add/test_add_return_type.py | 2 -- 9 files changed, 22 deletions(-) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py index eff8f4af1..7cd0f09f2 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py @@ -1,7 +1,3 @@ -"""Tests for $add date arithmetic including numeric offsets, rounding boundaries, operand -position, and sign handling. -""" - from datetime import datetime, timedelta, timezone import pytest diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py index 127f0e5de..f5c817b3d 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py @@ -1,5 +1,3 @@ -"""Tests for $add error cases including invalid operand types, multiple dates, and date overflow.""" - from datetime import datetime, timezone import pytest diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py index fa2f73df4..7019c1b0b 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py @@ -1,5 +1,3 @@ -"""Tests for $add input forms including nested expressions and mixed literal/field operands.""" - import pytest from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py index 958818e98..0343c3316 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py @@ -1,5 +1,3 @@ -"""Tests for $add infinity and NaN propagation.""" - import math import pytest diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py index 3854d72d8..9651d52d3 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py @@ -1,5 +1,3 @@ -"""Tests for $add null and missing field propagation.""" - from datetime import datetime, timezone import pytest diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py index 971b01581..3c4d6f305 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py @@ -1,7 +1,3 @@ -"""Tests for $add numeric operations including same-type and mixed-type addition, multiple -operands, empty/single operands, and sign handling. -""" - import pytest from bson import Decimal128, Int64 diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py index 2da84306e..29a992905 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py @@ -1,5 +1,3 @@ -"""Tests for $add integer and double overflow and underflow promotion.""" - import pytest from bson import Int64 diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py index 6658eb03a..f0cdd34ab 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py @@ -1,5 +1,3 @@ -"""Tests for $add decimal128 precision and boundary value handling.""" - import pytest from bson import Decimal128 diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py index d681c7fd2..7af9e0b66 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py @@ -1,5 +1,3 @@ -"""Tests for $add return type promotion rules across numeric and date operand combinations.""" - from datetime import datetime, timezone import pytest From f851712aaa40362d3ada515b2c50fd52ffc9b3c4 Mon Sep 17 00:00:00 2001 From: PatersonProjects Date: Tue, 30 Jun 2026 13:47:24 -0700 Subject: [PATCH 5/6] Revert "Added tests" This reverts commit 51a482ab4db4faa743125c3ac8699d126a8fbf33. Signed-off-by: PatersonProjects --- .../arithmetic/add/test_add_date.py | 162 ----------- .../arithmetic/add/test_add_errors.py | 203 -------------- .../arithmetic/add/test_add_input_forms.py | 47 ---- .../arithmetic/add/test_add_non_finite.py | 154 ----------- .../arithmetic/add/test_add_null.py | 86 ------ .../arithmetic/add/test_add_numeric.py | 259 ------------------ .../arithmetic/add/test_add_overflow.py | 95 ------- .../arithmetic/add/test_add_precision.py | 103 ------- .../arithmetic/add/test_add_return_type.py | 114 -------- 9 files changed, 1223 deletions(-) delete mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py delete mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py delete mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py delete mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py delete mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py delete mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py delete mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py delete mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py delete mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py deleted file mode 100644 index 7cd0f09f2..000000000 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_date.py +++ /dev/null @@ -1,162 +0,0 @@ -from datetime import datetime, timedelta, timezone - -import pytest -from bson import Decimal128, Int64 - -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 - ExpressionTestCase, -) -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( - assert_expression_result, - execute_expression_with_insert, -) -from documentdb_tests.framework.parametrize import pytest_params - -# Property [Date Arithmetic]: $add accepts exactly one date operand and one or more numeric -# operands (in milliseconds). The date may appear in any position. -ADD_DATE_NUMERIC_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "date_int32", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 86400000}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 2, tzinfo=timezone.utc), - msg="$add should add int32 milliseconds to a date", - ), - ExpressionTestCase( - "date_int64", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": Int64(86400000)}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 2, tzinfo=timezone.utc), - msg="$add should add int64 milliseconds to a date", - ), - ExpressionTestCase( - "date_decimal", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": Decimal128("1.5")}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 1, 0, 0, 0, 2000, tzinfo=timezone.utc), - msg="$add should round a decimal128 fractional millisecond value when adding to a date", - ), - ExpressionTestCase( - "date_double_round_up", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 2.5}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 1, 0, 0, 0, 3000, tzinfo=timezone.utc), - msg="$add should round up a double fractional millisecond value (.5) when adding to a date", - ), - ExpressionTestCase( - "date_double_truncates", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 4.4}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 1, 0, 0, 0, 4000, tzinfo=timezone.utc), - msg="$add should truncate a double fractional millisecond value (<.5) when adding to a date", # noqa: E501 - ), -] - -# Property [Date Rounding Boundaries]: $add rounds fractional millisecond offsets using -# round-half-away-from-zero. Values with |frac| < 0.5 truncate toward zero; values with -# |frac| >= 0.5 round away from zero. -ADD_DATE_ROUNDING_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "date_double_0_1", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 0.1}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 1, tzinfo=timezone.utc), - msg="$add should truncate 0.1ms and leave the date unchanged", - ), - ExpressionTestCase( - "date_double_0_49", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 0.49}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 1, tzinfo=timezone.utc), - msg="$add should truncate 0.49ms and leave the date unchanged", - ), - ExpressionTestCase( - "date_double_0_51", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 0.51}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 1, 0, 0, 0, 1000, tzinfo=timezone.utc), - msg="$add should round up 0.51ms to 1ms when adding to a date", - ), - ExpressionTestCase( - "date_double_0_6", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 0.6}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 1, 0, 0, 0, 1000, tzinfo=timezone.utc), - msg="$add should round up 0.6ms to 1ms when adding to a date", - ), - ExpressionTestCase( - "date_double_neg_0_5", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": -0.5}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 1, tzinfo=timezone.utc) - timedelta(milliseconds=1), - msg="$add should round -0.5ms away from zero to -1ms when adding to a date", - ), - ExpressionTestCase( - "date_double_neg_0_51", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": -0.51}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 1, tzinfo=timezone.utc) - timedelta(milliseconds=1), - msg="$add should round -0.51ms away from zero to -1ms when adding to a date", - ), -] - -# Property [Date Operand Position]: the date operand may appear in any position among the -# operands; only one date is permitted. -ADD_DATE_POSITION_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "number_then_date", - doc={"a": 86400000, "b": datetime(2026, 1, 1, tzinfo=timezone.utc)}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 2, tzinfo=timezone.utc), - msg="$add should add a date when the numeric operand appears before the date", - ), - ExpressionTestCase( - "date_in_middle", - doc={"a": 1, "b": datetime(2026, 1, 1, tzinfo=timezone.utc), "c": 1000}, - expression={"$add": ["$a", "$b", "$c"]}, - expected=datetime(2026, 1, 1, 0, 0, 1, 1000, tzinfo=timezone.utc), - msg="$add should add a date when it appears in the middle of the operand list", - ), -] - -# Property [Date Sign Handling]: adding zero or a negative number of milliseconds to a date -# returns the date unchanged or subtracted. -ADD_DATE_SIGN_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "date_negative", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": -86400000}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2025, 12, 31, tzinfo=timezone.utc), - msg="$add should subtract milliseconds from a date when adding a negative number", - ), - ExpressionTestCase( - "date_zero", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 0}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 1, tzinfo=timezone.utc), - msg="$add should return the same date when adding zero milliseconds", - ), - ExpressionTestCase( - "date_negative_zero", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": -0.0}, - expression={"$add": ["$a", "$b"]}, - expected=datetime(2026, 1, 1, tzinfo=timezone.utc), - msg="$add should return the same date when adding negative zero", - ), -] - -ADD_DATE_ALL_TESTS = ( - ADD_DATE_NUMERIC_TESTS + ADD_DATE_POSITION_TESTS + ADD_DATE_SIGN_TESTS + ADD_DATE_ROUNDING_TESTS -) - - -@pytest.mark.parametrize("test_case", pytest_params(ADD_DATE_ALL_TESTS)) -def test_add_date(collection, test_case: ExpressionTestCase): - """Test $add date arithmetic: numeric types, operand position, and sign handling.""" - result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) - assert_expression_result( - result, - expected=test_case.expected, - error_code=test_case.error_code, - msg=test_case.msg, - ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py deleted file mode 100644 index f5c817b3d..000000000 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_errors.py +++ /dev/null @@ -1,203 +0,0 @@ -from datetime import datetime, timezone - -import pytest -from bson import Binary, Code, MaxKey, MinKey, ObjectId, Regex, Timestamp - -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 - ExpressionTestCase, -) -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( - assert_expression_result, - execute_expression_with_insert, -) -from documentdb_tests.framework.error_codes import ( - MORE_THAN_ONE_DATE_ERROR, - OVERFLOW_ERROR, - TYPE_MISMATCH_ERROR, -) -from documentdb_tests.framework.parametrize import pytest_params -from documentdb_tests.framework.test_constants import ( - DECIMAL128_INFINITY, - DECIMAL128_NAN, - FLOAT_INFINITY, - FLOAT_NAN, - INT64_MAX, -) - -# Property [Type Strictness]: $add rejects non-numeric, non-date operand types. -ADD_TYPE_ERROR_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - f"type_{tid}", - doc={"a": 1, "b": val}, - expression={"$add": ["$a", "$b"]}, - error_code=TYPE_MISMATCH_ERROR, - msg=f"$add should reject a {tid} operand", - ) - for tid, val in [ - ("string", "string"), - ("bool", True), - ("array", [2, 3]), - ("object", {"a": 2}), - ("regex", Regex("abc")), - ("objectid", ObjectId("507f1f77bcf86cd799439011")), - ("binary", Binary(b"data")), - ("minkey", MinKey()), - ("maxkey", MaxKey()), - ("timestamp", Timestamp(1, 1)), - ("code", Code("function(){}")), - ] -] - -# Property [Mixed Valid and Invalid]: $add rejects an invalid operand when it appears among -# valid numeric operands. -ADD_MIXED_VALID_INVALID_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "mixed_valid_invalid", - doc={"a": 1, "b": 2, "c": "string"}, - expression={"$add": ["$a", "$b", "$c"]}, - error_code=TYPE_MISMATCH_ERROR, - msg="$add should error when a string appears among numeric operands", - ), -] - -# Property [Single Invalid Operand]: $add rejects a single operand of an invalid type. -ADD_SINGLE_TYPE_ERROR_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "single_string", - doc={"a": "string"}, - expression={"$add": ["$a"]}, - error_code=TYPE_MISMATCH_ERROR, - msg="$add should reject a single string operand", - ), - ExpressionTestCase( - "single_boolean", - doc={"a": True}, - expression={"$add": ["$a"]}, - error_code=TYPE_MISMATCH_ERROR, - msg="$add should reject a single boolean operand", - ), - ExpressionTestCase( - "single_array", - doc={"a": [1, 2]}, - expression={"$add": ["$a"]}, - error_code=TYPE_MISMATCH_ERROR, - msg="$add should reject a single array operand", - ), - ExpressionTestCase( - "single_object", - doc={"a": {"x": 1}}, - expression={"$add": ["$a"]}, - error_code=TYPE_MISMATCH_ERROR, - msg="$add should reject a single object operand", - ), -] - -# Property [Multiple Dates]: $add rejects expressions with more than one date operand. -ADD_MULTIPLE_DATE_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "add_two_identical_dates", - doc={ - "a": datetime(2026, 1, 1, tzinfo=timezone.utc), - "b": datetime(2026, 1, 1, tzinfo=timezone.utc), - }, - expression={"$add": ["$a", "$b"]}, - error_code=MORE_THAN_ONE_DATE_ERROR, - msg="$add should error when adding two identical date operands", - ), - ExpressionTestCase( - "two_different_dates", - doc={ - "a": datetime(2026, 1, 1, tzinfo=timezone.utc), - "b": datetime(2026, 1, 2, tzinfo=timezone.utc), - }, - expression={"$add": ["$a", "$b"]}, - error_code=MORE_THAN_ONE_DATE_ERROR, - msg="$add should error when adding two different date operands", - ), - ExpressionTestCase( - "two_dates_with_numbers", - doc={ - "a": datetime(2026, 1, 1, tzinfo=timezone.utc), - "b": datetime(2026, 1, 2, tzinfo=timezone.utc), - }, - expression={"$add": [1, 2, 3, "$a", "$b"]}, - error_code=MORE_THAN_ONE_DATE_ERROR, - msg="$add should error when two dates appear among numeric operands", - ), - ExpressionTestCase( - "dates_separated_by_number", - doc={ - "a": datetime(2026, 1, 1, tzinfo=timezone.utc), - "b": datetime(2026, 1, 2, tzinfo=timezone.utc), - }, - expression={"$add": ["$a", 1, "$b"]}, - error_code=MORE_THAN_ONE_DATE_ERROR, - msg="$add should error when two dates are separated by a numeric operand", - ), -] - -# Property [Date with Non-Finite]: $add rejects NaN and Infinity as numeric operands when a -# date is also present, since the resulting date would be non-representable. -ADD_DATE_NON_FINITE_ERROR_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "date_nan", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": FLOAT_NAN}, - expression={"$add": ["$a", "$b"]}, - error_code=OVERFLOW_ERROR, - msg="$add should error when adding a date and float NaN", - ), - ExpressionTestCase( - "date_infinity", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": FLOAT_INFINITY}, - expression={"$add": ["$a", "$b"]}, - error_code=OVERFLOW_ERROR, - msg="$add should error when adding a date and float infinity", - ), - ExpressionTestCase( - "date_decimal_nan", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": DECIMAL128_NAN}, - expression={"$add": ["$a", "$b"]}, - error_code=OVERFLOW_ERROR, - msg="$add should error when adding a date and decimal128 NaN", - ), - ExpressionTestCase( - "date_decimal_infinity", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": DECIMAL128_INFINITY}, - expression={"$add": ["$a", "$b"]}, - error_code=OVERFLOW_ERROR, - msg="$add should error when adding a date and decimal128 infinity", - ), -] - -# Property [Date Overflow]: $add errors when the millisecond offset would push the date result -# beyond the representable date range. -ADD_DATE_OVERFLOW_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "date_int64_max", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": INT64_MAX}, - expression={"$add": ["$a", "$b"]}, - error_code=OVERFLOW_ERROR, - msg="$add should error when adding INT64_MAX milliseconds to a date overflows the date range", # noqa: E501 - ), -] - -ADD_ERROR_ALL_TESTS = ( - ADD_TYPE_ERROR_TESTS - + ADD_MIXED_VALID_INVALID_TESTS - + ADD_SINGLE_TYPE_ERROR_TESTS - + ADD_MULTIPLE_DATE_TESTS - + ADD_DATE_NON_FINITE_ERROR_TESTS - + ADD_DATE_OVERFLOW_TESTS -) - - -@pytest.mark.parametrize("test_case", pytest_params(ADD_ERROR_ALL_TESTS)) -def test_add_errors(collection, test_case: ExpressionTestCase): - """Test $add type, multiple-date, and date non-finite error cases.""" - result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) - assert_expression_result( - result, - expected=test_case.expected, - error_code=test_case.error_code, - msg=test_case.msg, - ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py deleted file mode 100644 index 7019c1b0b..000000000 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_input_forms.py +++ /dev/null @@ -1,47 +0,0 @@ -import pytest - -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 - ExpressionTestCase, -) -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( - assert_expression_result, - execute_expression_with_insert, -) -from documentdb_tests.framework.parametrize import pytest_params - -# Property [Expression Input]: $add evaluates a nested expression argument before summing. -ADD_EXPRESSION_INPUT_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "nested_add", - doc={"a": 1, "b": 2}, - expression={"$add": [{"$add": ["$a", "$b"]}, 3]}, - expected=6, - msg="$add should evaluate a nested $add expression as an operand", - ), -] - -# Property [Mixed Literal and Field]: $add accepts a mix of field references and inline literals -# in the same operand list. -ADD_MIXED_INPUT_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "mixed_literal_and_field", - doc={"a": 10}, - expression={"$add": ["$a", 5]}, - expected=15, - msg="$add should sum a field reference and an inline literal operand", - ), -] - -ADD_INPUT_FORM_ALL_TESTS = ADD_EXPRESSION_INPUT_TESTS + ADD_MIXED_INPUT_TESTS - - -@pytest.mark.parametrize("test_case", pytest_params(ADD_INPUT_FORM_ALL_TESTS)) -def test_add_input_forms(collection, test_case: ExpressionTestCase): - """Test $add literal, nested expression, and mixed input form cases.""" - result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) - assert_expression_result( - result, - expected=test_case.expected, - error_code=test_case.error_code, - msg=test_case.msg, - ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py deleted file mode 100644 index 0343c3316..000000000 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_non_finite.py +++ /dev/null @@ -1,154 +0,0 @@ -import math - -import pytest - -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 - ExpressionTestCase, -) -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( - assert_expression_result, - execute_expression_with_insert, -) -from documentdb_tests.framework.parametrize import pytest_params -from documentdb_tests.framework.test_constants import ( - DECIMAL128_INFINITY, - DECIMAL128_NAN, - DECIMAL128_NEGATIVE_INFINITY, - FLOAT_INFINITY, - FLOAT_NAN, - FLOAT_NEGATIVE_INFINITY, -) - -# Property [Infinity]: $add propagates infinity according to IEEE 754 rules. -ADD_INFINITY_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "infinity", - doc={"a": FLOAT_INFINITY, "b": 1}, - expression={"$add": ["$a", "$b"]}, - expected=FLOAT_INFINITY, - msg="$add should return infinity when adding infinity and a finite number", - ), - ExpressionTestCase( - "negative_infinity", - doc={"a": FLOAT_NEGATIVE_INFINITY, "b": 1}, - expression={"$add": ["$a", "$b"]}, - expected=FLOAT_NEGATIVE_INFINITY, - msg="$add should return negative infinity when adding negative infinity and a finite number", # noqa: E501 - ), - ExpressionTestCase( - "single_infinity", - doc={"a": FLOAT_INFINITY}, - expression={"$add": ["$a"]}, - expected=FLOAT_INFINITY, - msg="$add should return infinity for a single infinity operand", - ), - ExpressionTestCase( - "inf_plus_inf", - doc={"a": FLOAT_INFINITY, "b": FLOAT_INFINITY}, - expression={"$add": ["$a", "$b"]}, - expected=FLOAT_INFINITY, - msg="$add should return infinity when adding two positive infinities", - ), - ExpressionTestCase( - "neg_inf_plus_neg_inf", - doc={"a": FLOAT_NEGATIVE_INFINITY, "b": FLOAT_NEGATIVE_INFINITY}, - expression={"$add": ["$a", "$b"]}, - expected=FLOAT_NEGATIVE_INFINITY, - msg="$add should return negative infinity when adding two negative infinities", - ), - ExpressionTestCase( - "inf_plus_zero", - doc={"a": FLOAT_INFINITY, "b": 0}, - expression={"$add": ["$a", "$b"]}, - expected=FLOAT_INFINITY, - msg="$add should return infinity when adding infinity and zero", - ), - ExpressionTestCase( - "neg_inf_plus_zero", - doc={"a": FLOAT_NEGATIVE_INFINITY, "b": 0}, - expression={"$add": ["$a", "$b"]}, - expected=FLOAT_NEGATIVE_INFINITY, - msg="$add should return negative infinity when adding negative infinity and zero", - ), - ExpressionTestCase( - "decimal_infinity", - doc={"a": DECIMAL128_INFINITY, "b": 1}, - expression={"$add": ["$a", "$b"]}, - expected=DECIMAL128_INFINITY, - msg="$add should return decimal128 infinity when adding decimal128 infinity and a number", - ), - ExpressionTestCase( - "decimal_negative_infinity", - doc={"a": DECIMAL128_NEGATIVE_INFINITY, "b": 1}, - expression={"$add": ["$a", "$b"]}, - expected=DECIMAL128_NEGATIVE_INFINITY, - msg="$add should return decimal128 negative infinity when adding decimal128 negative infinity and a number", # noqa: E501 - ), -] - -# Property [NaN]: $add propagates NaN according to IEEE 754 rules. -ADD_NAN_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "nan_add_one", - doc={"a": FLOAT_NAN, "b": 1}, - expression={"$add": ["$a", "$b"]}, - expected=pytest.approx(math.nan, nan_ok=True), - msg="$add should return NaN when adding float NaN and a finite number", - ), - ExpressionTestCase( - "inf_minus_inf", - doc={"a": FLOAT_INFINITY, "b": FLOAT_NEGATIVE_INFINITY}, - expression={"$add": ["$a", "$b"]}, - expected=pytest.approx(math.nan, nan_ok=True), - msg="$add should return NaN when adding float infinity and negative infinity", - ), - ExpressionTestCase( - "nan_plus_nan", - doc={"a": FLOAT_NAN, "b": FLOAT_NAN}, - expression={"$add": ["$a", "$b"]}, - expected=pytest.approx(math.nan, nan_ok=True), - msg="$add should return NaN when adding two float NaN values", - ), - ExpressionTestCase( - "nan_plus_inf", - doc={"a": FLOAT_NAN, "b": FLOAT_INFINITY}, - expression={"$add": ["$a", "$b"]}, - expected=pytest.approx(math.nan, nan_ok=True), - msg="$add should return NaN when adding float NaN and infinity", - ), - ExpressionTestCase( - "decimal_nan", - doc={"a": DECIMAL128_NAN, "b": 1}, - expression={"$add": ["$a", "$b"]}, - expected=DECIMAL128_NAN, - msg="$add should return decimal128 NaN when adding decimal128 NaN and a number", - ), - ExpressionTestCase( - "decimal_nan_plus_nan", - doc={"a": DECIMAL128_NAN, "b": DECIMAL128_NAN}, - expression={"$add": ["$a", "$b"]}, - expected=DECIMAL128_NAN, - msg="$add should return decimal128 NaN when adding two decimal128 NaN values", - ), - ExpressionTestCase( - "decimal_inf_minus_inf", - doc={"a": DECIMAL128_INFINITY, "b": DECIMAL128_NEGATIVE_INFINITY}, - expression={"$add": ["$a", "$b"]}, - expected=DECIMAL128_NAN, - msg="$add should return decimal128 NaN when adding decimal128 infinity and negative infinity", # noqa: E501 - ), -] - -ADD_NON_FINITE_ALL_TESTS = ADD_INFINITY_TESTS + ADD_NAN_TESTS - - -@pytest.mark.parametrize("test_case", pytest_params(ADD_NON_FINITE_ALL_TESTS)) -def test_add_non_finite(collection, test_case: ExpressionTestCase): - """Test $add infinity and NaN propagation cases.""" - result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) - assert_expression_result( - result, - expected=test_case.expected, - error_code=test_case.error_code, - msg=test_case.msg, - ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py deleted file mode 100644 index 9651d52d3..000000000 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_null.py +++ /dev/null @@ -1,86 +0,0 @@ -from datetime import datetime, timezone - -import pytest - -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 - ExpressionTestCase, -) -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( - assert_expression_result, - execute_expression_with_insert, -) -from documentdb_tests.framework.parametrize import pytest_params -from documentdb_tests.framework.test_constants import MISSING - -# Property [Null Propagation]: $add returns null if any operand is null or refers to a missing -# field. -ADD_NULL_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "single_null", - doc={"a": None}, - expression={"$add": ["$a"]}, - expected=None, - msg="$add should return null for a single null operand", - ), - ExpressionTestCase( - "null_operand", - doc={"a": 1, "b": None}, - expression={"$add": ["$a", "$b"]}, - expected=None, - msg="$add should return null when any operand is null", - ), - ExpressionTestCase( - "missing_field", - doc={}, - expression={"$add": [1, MISSING]}, - expected=None, - msg="$add should return null when any operand is a missing field", - ), - ExpressionTestCase( - "null_with_multiple", - doc={"a": 1, "b": 2, "c": None}, - expression={"$add": ["$a", "$b", "$c"]}, - expected=None, - msg="$add should return null when null appears among multiple operands", - ), - ExpressionTestCase( - "null_in_middle", - doc={"a": 1, "b": 2, "c": 3, "e": 5}, - expression={"$add": ["$a", "$b", "$c", None, "$e"]}, - expected=None, - msg="$add should return null when null appears in the middle of operands", - ), - ExpressionTestCase( - "all_null", - doc={"a": None, "b": None}, - expression={"$add": ["$a", "$b"]}, - expected=None, - msg="$add should return null when all operands are null", - ), - ExpressionTestCase( - "all_missing", - doc={}, - expression={"$add": [MISSING, MISSING]}, - expected=None, - msg="$add should return null when all operands are missing fields", - ), - ExpressionTestCase( - "date_and_null", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": None}, - expression={"$add": ["$a", "$b"]}, - expected=None, - msg="$add should return null when a date is paired with a null operand", - ), -] - - -@pytest.mark.parametrize("test_case", pytest_params(ADD_NULL_TESTS)) -def test_add_null(collection, test_case: ExpressionTestCase): - """Test $add null and missing field propagation cases.""" - result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) - assert_expression_result( - result, - expected=test_case.expected, - error_code=test_case.error_code, - msg=test_case.msg, - ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py deleted file mode 100644 index 3c4d6f305..000000000 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_numeric.py +++ /dev/null @@ -1,259 +0,0 @@ -import pytest -from bson import Decimal128, Int64 - -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 - ExpressionTestCase, -) -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( - assert_expression_result, - execute_expression_with_insert, -) -from documentdb_tests.framework.parametrize import pytest_params - -# Property [Same-Type Addition]: $add of two values of the same numeric type returns a value of -# that type. -ADD_SAME_TYPE_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "same_type_int32", - doc={"a": 1, "b": 2}, - expression={"$add": ["$a", "$b"]}, - expected=3, - msg="$add should add two int32 values", - ), - ExpressionTestCase( - "same_type_int64", - doc={"a": Int64(10), "b": Int64(20)}, - expression={"$add": ["$a", "$b"]}, - expected=Int64(30), - msg="$add should add two int64 values", - ), - ExpressionTestCase( - "same_type_double", - doc={"a": 1.5, "b": 2.5}, - expression={"$add": ["$a", "$b"]}, - expected=4.0, - msg="$add should add two double values", - ), - ExpressionTestCase( - "same_type_decimal", - doc={"a": Decimal128("10.5"), "b": Decimal128("20.5")}, - expression={"$add": ["$a", "$b"]}, - expected=Decimal128("31.0"), - msg="$add should add two decimal128 values", - ), -] - -# Property [Type Promotion]: $add promotes to the wider type when operands have different numeric -# types. Precedence: decimal128 > double > int64 > int32. -ADD_MIXED_TYPE_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "int32_int64", - doc={"a": 1, "b": Int64(20)}, - expression={"$add": ["$a", "$b"]}, - expected=Int64(21), - msg="$add should return int64 when adding int32 and int64", - ), - ExpressionTestCase( - "int32_double", - doc={"a": 1, "b": 2.5}, - expression={"$add": ["$a", "$b"]}, - expected=3.5, - msg="$add should return double when adding int32 and double", - ), - ExpressionTestCase( - "int32_decimal", - doc={"a": 1, "b": Decimal128("2.5")}, - expression={"$add": ["$a", "$b"]}, - expected=Decimal128("3.5"), - msg="$add should return decimal128 when adding int32 and decimal128", - ), - ExpressionTestCase( - "int64_double", - doc={"a": Int64(10), "b": 2.5}, - expression={"$add": ["$a", "$b"]}, - expected=12.5, - msg="$add should return double when adding int64 and double", - ), - ExpressionTestCase( - "int64_decimal", - doc={"a": Int64(10), "b": Decimal128("2.5")}, - expression={"$add": ["$a", "$b"]}, - expected=Decimal128("12.5"), - msg="$add should return decimal128 when adding int64 and decimal128", - ), - ExpressionTestCase( - "double_decimal", - doc={"a": 1.5, "b": Decimal128("2.5")}, - expression={"$add": ["$a", "$b"]}, - expected=pytest.approx(Decimal128("4.00000000000000")), - msg="$add should return decimal128 when adding double and decimal128", - ), - ExpressionTestCase( - "three_mixed_types", - doc={"a": Decimal128("1.5"), "b": 2.5, "c": Int64(3)}, - expression={"$add": ["$a", "$b", "$c"]}, - expected=pytest.approx(Decimal128("7.00000000000000")), - msg="$add should return decimal128 when adding decimal128, double, and int64", - ), -] - -# Property [Multiple Operands]: $add correctly sums three or more operands. -ADD_MULTIPLE_OPERANDS_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "multiple_int32", - doc={"a": 1, "b": 2, "c": 3}, - expression={"$add": ["$a", "$b", "$c"]}, - expected=6, - msg="$add should add multiple int32 values", - ), - ExpressionTestCase( - "multiple_int64", - doc={"a": Int64(1), "b": Int64(2), "c": Int64(3)}, - expression={"$add": ["$a", "$b", "$c"]}, - expected=Int64(6), - msg="$add should add multiple int64 values", - ), - ExpressionTestCase( - "multiple_double", - doc={"a": 1.1, "b": 2.2, "c": 3.3}, - expression={"$add": ["$a", "$b", "$c"]}, - expected=pytest.approx(6.6), - msg="$add should add multiple double values", - ), - ExpressionTestCase( - "multiple_decimal", - doc={ - "a": Decimal128("1"), - "b": Decimal128("2"), - "c": Decimal128("3"), - "d": Decimal128("4"), - }, - expression={"$add": ["$a", "$b", "$c", "$d"]}, - expected=Decimal128("10"), - msg="$add should add multiple decimal128 values", - ), - ExpressionTestCase( - "five_operands", - doc={"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}, - expression={"$add": ["$a", "$b", "$c", "$d", "$e"]}, - expected=15, - msg="$add should correctly sum five int32 operands", - ), - ExpressionTestCase( - "ten_operands", - doc={ - "a": 1, - "b": 2, - "c": 3, - "d": 4, - "e": 5, - "f": 6, - "g": 7, - "h": 8, - "i": 9, - "j": 10, - }, - expression={"$add": ["$a", "$b", "$c", "$d", "$e", "$f", "$g", "$h", "$i", "$j"]}, - expected=55, - msg="$add should correctly sum ten int32 operands", - ), -] - -# Property [Empty and Single Operand]: $add of zero operands returns 0; single operand returns -# that value unchanged. -ADD_SINGLE_AND_EMPTY_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "empty", - doc={}, - expression={"$add": []}, - expected=0, - msg="$add should return 0 for empty operand list", - ), - ExpressionTestCase( - "single_int32", - doc={"a": 5}, - expression={"$add": ["$a"]}, - expected=5, - msg="$add should return the value for a single int32 operand", - ), - ExpressionTestCase( - "single_int64", - doc={"a": Int64(0)}, - expression={"$add": ["$a"]}, - expected=Int64(0), - msg="$add should return the value for a single int64 operand", - ), - ExpressionTestCase( - "single_double", - doc={"a": 0.0}, - expression={"$add": ["$a"]}, - expected=0.0, - msg="$add should return the value for a single double operand", - ), - ExpressionTestCase( - "single_decimal", - doc={"a": Decimal128("0")}, - expression={"$add": ["$a"]}, - expected=Decimal128("0"), - msg="$add should return the value for a single decimal128 operand", - ), -] - -# Property [Sign Handling]: $add handles negative values and zero correctly. -ADD_SIGN_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "negative_positive", - doc={"a": -5, "b": 3}, - expression={"$add": ["$a", "$b"]}, - expected=-2, - msg="$add should add a negative and a positive int32 value", - ), - ExpressionTestCase( - "both_negative", - doc={"a": -10, "b": -20}, - expression={"$add": ["$a", "$b"]}, - expected=-30, - msg="$add should add two negative int32 values", - ), - ExpressionTestCase( - "zeros", - doc={"a": 0, "b": 0}, - expression={"$add": ["$a", "$b"]}, - expected=0, - msg="$add should return 0 when adding two int32 zeros", - ), - ExpressionTestCase( - "zero_negative_zero", - doc={"a": 0, "b": -0.0}, - expression={"$add": ["$a", "$b"]}, - expected=0.0, - msg="$add should return 0.0 when adding int32 zero and negative zero double", - ), - ExpressionTestCase( - "sum_to_zero", - doc={"a": 1, "b": 0, "c": -1}, - expression={"$add": ["$a", "$b", "$c"]}, - expected=0, - msg="$add should return 0 when operands sum to zero", - ), -] - -ADD_NUMERIC_ALL_TESTS = ( - ADD_SAME_TYPE_TESTS - + ADD_MIXED_TYPE_TESTS - + ADD_MULTIPLE_OPERANDS_TESTS - + ADD_SINGLE_AND_EMPTY_TESTS - + ADD_SIGN_TESTS -) - - -@pytest.mark.parametrize("test_case", pytest_params(ADD_NUMERIC_ALL_TESTS)) -def test_add_numeric(collection, test_case: ExpressionTestCase): - """Test $add numeric type combinations, multiple operands, and sign handling.""" - result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) - assert_expression_result( - result, - expected=test_case.expected, - error_code=test_case.error_code, - msg=test_case.msg, - ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py deleted file mode 100644 index 29a992905..000000000 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_overflow.py +++ /dev/null @@ -1,95 +0,0 @@ -import pytest -from bson import Int64 - -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 - ExpressionTestCase, -) -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( - assert_expression_result, - execute_expression_with_insert, -) -from documentdb_tests.framework.parametrize import pytest_params -from documentdb_tests.framework.test_constants import ( - DOUBLE_FROM_INT64_MAX, - FLOAT_INFINITY, - FLOAT_NEGATIVE_INFINITY, - INT32_MAX, - INT32_MIN, - INT32_OVERFLOW, - INT32_UNDERFLOW, - INT64_MAX, - INT64_MIN, -) - -# Property [Int32 Overflow]: when an int32 result exceeds the int32 range, $add promotes to -# int64. -ADD_INT32_OVERFLOW_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "int32_overflow", - doc={"a": INT32_MAX, "b": 1}, - expression={"$add": ["$a", "$b"]}, - expected=Int64(INT32_OVERFLOW), - msg="$add should promote to int64 when the int32 result overflows INT32_MAX", - ), - ExpressionTestCase( - "int32_underflow", - doc={"a": INT32_MIN, "b": -1}, - expression={"$add": ["$a", "$b"]}, - expected=Int64(INT32_UNDERFLOW), - msg="$add should promote to int64 when the int32 result underflows INT32_MIN", - ), -] - -# Property [Int64 Overflow]: when an int64 result exceeds the int64 range, $add promotes to -# double. -ADD_INT64_OVERFLOW_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "int64_overflow", - doc={"a": INT64_MAX, "b": 1}, - expression={"$add": ["$a", "$b"]}, - expected=pytest.approx(DOUBLE_FROM_INT64_MAX), - msg="$add should promote to double when the int64 result overflows INT64_MAX", - ), - ExpressionTestCase( - "int64_underflow", - doc={"a": INT64_MIN, "b": -1}, - expression={"$add": ["$a", "$b"]}, - expected=pytest.approx(-DOUBLE_FROM_INT64_MAX), - msg="$add should promote to double when the int64 result underflows INT64_MIN", - ), -] - -# Property [Double Overflow]: when a double result exceeds the double range, $add returns -# infinity. -ADD_DOUBLE_OVERFLOW_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "double_overflow", - doc={"a": 1e308, "b": 1e308}, - expression={"$add": ["$a", "$b"]}, - expected=FLOAT_INFINITY, - msg="$add should return positive infinity on double overflow", - ), - ExpressionTestCase( - "double_underflow", - doc={"a": -1e308, "b": -1e308}, - expression={"$add": ["$a", "$b"]}, - expected=FLOAT_NEGATIVE_INFINITY, - msg="$add should return negative infinity on double underflow", - ), -] - -ADD_OVERFLOW_ALL_TESTS = ( - ADD_INT32_OVERFLOW_TESTS + ADD_INT64_OVERFLOW_TESTS + ADD_DOUBLE_OVERFLOW_TESTS -) - - -@pytest.mark.parametrize("test_case", pytest_params(ADD_OVERFLOW_ALL_TESTS)) -def test_add_overflow(collection, test_case: ExpressionTestCase): - """Test $add integer and double overflow and underflow cases.""" - result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) - assert_expression_result( - result, - expected=test_case.expected, - error_code=test_case.error_code, - msg=test_case.msg, - ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py deleted file mode 100644 index f0cdd34ab..000000000 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_precision.py +++ /dev/null @@ -1,103 +0,0 @@ -import pytest -from bson import Decimal128 - -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 - ExpressionTestCase, -) -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( - assert_expression_result, - execute_expression_with_insert, -) -from documentdb_tests.framework.parametrize import pytest_params -from documentdb_tests.framework.test_constants import ( - DECIMAL128_INFINITY, - DECIMAL128_MAX, - DECIMAL128_MIN, - DECIMAL128_NEGATIVE_INFINITY, -) - -# Property [Decimal128 Precision]: $add preserves decimal128 precision, including exact -# representation of values that are inexact in double. -ADD_DECIMAL_PRECISION_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "decimal_precision", - doc={"a": Decimal128("1.5"), "b": Decimal128("2.5")}, - expression={"$add": ["$a", "$b"]}, - expected=Decimal128("4.0"), - msg="$add should preserve decimal128 precision", - ), - ExpressionTestCase( - "decimal_precision_small", - doc={"a": Decimal128("0.1"), "b": Decimal128("0.2")}, - expression={"$add": ["$a", "$b"]}, - expected=Decimal128("0.3"), - msg="$add should exactly represent 0.1 + 0.2 with decimal128", - ), - ExpressionTestCase( - "decimal_large_precision", - doc={ - "a": Decimal128("999999999999999999999999999999999"), - "b": Decimal128("1"), - }, - expression={"$add": ["$a", "$b"]}, - expected=Decimal128("1000000000000000000000000000000000"), - msg="$add should handle large decimal128 addition with full precision", - ), - ExpressionTestCase( - "decimal_large_negative_precision", - doc={ - "a": Decimal128("-999999999999999999999999999999999"), - "b": Decimal128("-1"), - }, - expression={"$add": ["$a", "$b"]}, - expected=Decimal128("-1000000000000000000000000000000000"), - msg="$add should handle large negative decimal128 addition with full precision", - ), -] - -# Property [Decimal128 Boundaries]: $add at decimal128 boundary values promotes to infinity when -# the result overflows, and returns zero when max and min cancel. -ADD_DECIMAL_BOUNDARY_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "decimal128_max_plus_zero", - doc={"a": DECIMAL128_MAX, "b": Decimal128("0")}, - expression={"$add": ["$a", "$b"]}, - expected=DECIMAL128_MAX, - msg="$add should return decimal128 max when adding zero to decimal128 max", - ), - ExpressionTestCase( - "decimal128_max_plus_max", - doc={"a": DECIMAL128_MAX, "b": DECIMAL128_MAX}, - expression={"$add": ["$a", "$b"]}, - expected=DECIMAL128_INFINITY, - msg="$add should return decimal128 infinity when adding two decimal128 max values", - ), - ExpressionTestCase( - "decimal128_min_plus_min", - doc={"a": DECIMAL128_MIN, "b": DECIMAL128_MIN}, - expression={"$add": ["$a", "$b"]}, - expected=DECIMAL128_NEGATIVE_INFINITY, - msg="$add should return decimal128 negative infinity when adding two decimal128 min values", - ), - ExpressionTestCase( - "decimal128_max_plus_min", - doc={"a": DECIMAL128_MAX, "b": DECIMAL128_MIN}, - expression={"$add": ["$a", "$b"]}, - expected=Decimal128("0E+6111"), - msg="$add should return zero when adding decimal128 max and decimal128 min", - ), -] - -ADD_PRECISION_ALL_TESTS = ADD_DECIMAL_PRECISION_TESTS + ADD_DECIMAL_BOUNDARY_TESTS - - -@pytest.mark.parametrize("test_case", pytest_params(ADD_PRECISION_ALL_TESTS)) -def test_add_precision(collection, test_case: ExpressionTestCase): - """Test $add decimal128 precision and boundary value cases.""" - result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) - assert_expression_result( - result, - expected=test_case.expected, - error_code=test_case.error_code, - msg=test_case.msg, - ) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py b/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py deleted file mode 100644 index 7af9e0b66..000000000 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/arithmetic/add/test_add_return_type.py +++ /dev/null @@ -1,114 +0,0 @@ -from datetime import datetime, timezone - -import pytest -from bson import Decimal128, Int64 - -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.expression_test_case import ( # noqa: E501 - ExpressionTestCase, -) -from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( - assert_expression_result, - execute_expression_with_insert, -) -from documentdb_tests.framework.parametrize import pytest_params - -# Property [Return Type]: $add follows numeric type promotion rules to determine the result type. -# Precedence: decimal128 > double > int64 > int32. Date + numeric always returns date. -ADD_RETURN_TYPE_TESTS: list[ExpressionTestCase] = [ - ExpressionTestCase( - "return_type_int_int", - doc={"a": 1, "b": 2}, - expression={"$type": {"$add": ["$a", "$b"]}}, - expected="int", - msg="$add should return int type when adding two int32 values", - ), - ExpressionTestCase( - "return_type_int_long", - doc={"a": 1, "b": Int64(2)}, - expression={"$type": {"$add": ["$a", "$b"]}}, - expected="long", - msg="$add should return long type when adding int32 and int64", - ), - ExpressionTestCase( - "return_type_int_double", - doc={"a": 1, "b": 2.0}, - expression={"$type": {"$add": ["$a", "$b"]}}, - expected="double", - msg="$add should return double type when adding int32 and double", - ), - ExpressionTestCase( - "return_type_int_decimal", - doc={"a": 1, "b": Decimal128("2")}, - expression={"$type": {"$add": ["$a", "$b"]}}, - expected="decimal", - msg="$add should return decimal type when adding int32 and decimal128", - ), - ExpressionTestCase( - "return_type_long_long", - doc={"a": Int64(1), "b": Int64(2)}, - expression={"$type": {"$add": ["$a", "$b"]}}, - expected="long", - msg="$add should return long type when adding two int64 values", - ), - ExpressionTestCase( - "return_type_long_double", - doc={"a": Int64(1), "b": 2.0}, - expression={"$type": {"$add": ["$a", "$b"]}}, - expected="double", - msg="$add should return double type when adding int64 and double", - ), - ExpressionTestCase( - "return_type_long_decimal", - doc={"a": Int64(1), "b": Decimal128("2")}, - expression={"$type": {"$add": ["$a", "$b"]}}, - expected="decimal", - msg="$add should return decimal type when adding int64 and decimal128", - ), - ExpressionTestCase( - "return_type_double_double", - doc={"a": 1.0, "b": 2.0}, - expression={"$type": {"$add": ["$a", "$b"]}}, - expected="double", - msg="$add should return double type when adding two double values", - ), - ExpressionTestCase( - "return_type_double_decimal", - doc={"a": 1.0, "b": Decimal128("2")}, - expression={"$type": {"$add": ["$a", "$b"]}}, - expected="decimal", - msg="$add should return decimal type when adding double and decimal128", - ), - ExpressionTestCase( - "return_type_decimal_decimal", - doc={"a": Decimal128("1"), "b": Decimal128("2")}, - expression={"$type": {"$add": ["$a", "$b"]}}, - expected="decimal", - msg="$add should return decimal type when adding two decimal128 values", - ), - ExpressionTestCase( - "return_type_date_int", - doc={"a": datetime(2026, 1, 1, tzinfo=timezone.utc), "b": 1000}, - expression={"$type": {"$add": ["$a", "$b"]}}, - expected="date", - msg="$add should return date type when adding a date and an int32", - ), - ExpressionTestCase( - "return_type_empty", - doc={}, - expression={"$type": {"$add": []}}, - expected="int", - msg="$add should return int type for an empty operand list", - ), -] - - -@pytest.mark.parametrize("test_case", pytest_params(ADD_RETURN_TYPE_TESTS)) -def test_add_return_type(collection, test_case: ExpressionTestCase): - """Test $add return type promotion rules for all numeric type combinations.""" - result = execute_expression_with_insert(collection, test_case.expression, test_case.doc) - assert_expression_result( - result, - expected=test_case.expected, - error_code=test_case.error_code, - msg=test_case.msg, - ) From 80b81b721b6199b5b718108aa9f7fc2a8316904c Mon Sep 17 00:00:00 2001 From: PatersonProjects Date: Thu, 2 Jul 2026 13:59:01 -0700 Subject: [PATCH 6/6] Addressed PR Comment Signed-off-by: PatersonProjects --- .../diagnostic/commands/ping/test_ping_core_behavior.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_core_behavior.py b/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_core_behavior.py index 56d39e3cf..6ca0adb9d 100644 --- a/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_core_behavior.py +++ b/documentdb_tests/compatibility/tests/system/diagnostic/commands/ping/test_ping_core_behavior.py @@ -50,9 +50,9 @@ def test_ping_multiple_times_in_succession(collection): """Test ping can be executed multiple times in succession.""" for _ in range(3): result = execute_admin_command(collection, {"ping": 1}) - assertProperties( - result, {"ok": Eq(1.0)}, msg="Should succeed on repeated execution", raw_res=True - ) + assertProperties( + result, {"ok": Eq(1.0)}, msg="Should succeed on repeated execution", raw_res=True + ) def test_ping_after_write_activity(collection):