From 9d3072c4232ce1ff381488df1c0f754869f34cb5 Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 19 Jun 2026 16:13:13 -0400 Subject: [PATCH 01/13] Add test file for stdDevSamp expression test Signed-off-by: Jay --- .../accumulator/stdDevSamp/test_expression_stdDevSamp.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp.py diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp.py new file mode 100644 index 000000000..e69de29bb From 357b8c8eec22b9451648b1953d51eb1ccd09c4f5 Mon Sep 17 00:00:00 2001 From: Jay Date: Sat, 20 Jun 2026 19:18:29 -0400 Subject: [PATCH 02/13] Add $stdDevSamp core tests Signed-off-by: Jay --- .../stdDevSamp/test_expression_stdDevSamp.py | 0 .../test_expression_stdDevSamp_core.py | 173 ++++++++++++++++++ 2 files changed, 173 insertions(+) delete mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp.py create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py new file mode 100644 index 000000000..1276fca79 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py @@ -0,0 +1,173 @@ +import math +from dataclasses import dataclass +from typing import Any + +import pytest +from bson import Decimal128, Int64 + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_case import BaseTestCase +from documentdb_tests.framework.test_constants import ( + DECIMAL128_NAN, + DECIMAL128_NEGATIVE_NAN, + DOUBLE_NEGATIVE_ZERO, + FLOAT_NAN, +) + + +@dataclass(frozen=True) +class StdDevSampTest(BaseTestCase): + values: Any = None + + +STDDEVSAMP_CORE_TESTS: list[StdDevSampTest] = [ + # same type operations + StdDevSampTest( + "core_numeric_int32", + values=[1, 2, 3, 4], + expected=pytest.approx(1.2909944487358056), + msg="Should compute sample std dev of int32 values", + ), + StdDevSampTest( + "core_numeric_int64", + values=[Int64(1), Int64(3), Int64(2), Int64(4)], + expected=pytest.approx(1.2909944487358056), + msg="Should compute sample std dev of int64 values", + ), + StdDevSampTest( + "core_numeric_double", + values=[1.0, 2.0, 4.0, 3.0], + expected=pytest.approx(1.2909944487358056), + msg="Should compute sample std dev of double values", + ), + StdDevSampTest( + "core_numeric_decimal128", + values=[Decimal128("1"), Decimal128("2"), Decimal128("3"), Decimal128("4")], + expected=pytest.approx(1.2909944487358056), + msg="Should compute sample std dev of decimal128 values", + ), + # mix type operation + StdDevSampTest( + "core_numeric_mix", + values=[Decimal128("1"), Int64(2), 3, 4.0], + expected=pytest.approx(1.2909944487358056), + msg="Should compute sample std dev of mix numerical values", + ), + # negative operation + StdDevSampTest( + "core_numeric_negative", + values=[-1, -2, -3, -4], + expected=pytest.approx(1.2909944487358056), + msg="Should compute sample std dev of negative values", + ), + StdDevSampTest( + "core_numeric_mixed_signs", + values=[-3, 3, 9], + expected=pytest.approx(6.0), + msg="Should compute sample std dev of both positive and negative values", + ), + StdDevSampTest( + "core_negative_zero", + values=[DOUBLE_NEGATIVE_ZERO, DOUBLE_NEGATIVE_ZERO], + expected=0.0, + msg="Should compute sample std dev treating negative zero as zero", + ), + # large N + StdDevSampTest( + "core_large_n", + values=list(range(1000)), + expected=pytest.approx(288.8194360957494), + msg="Should compute sample std dev of all 1000 values", + ), + # N<2 -> null rule + StdDevSampTest( + "core_single_value", + values=[5], + expected=None, + msg="Should return null when single value", + ), + StdDevSampTest( + "core_no_value", + values=[], + expected=None, + msg="Should return null when no values", + ), + StdDevSampTest( + "core_scalar_value", + values=42, + expected=None, + msg="Should return null when scalar value", + ), + # zero variance + StdDevSampTest( + "core_zero_variance", + values=[5, 5, 5], + expected=0.0, + msg="Should return 0.0 when there's no variance", + ), + # NaN & Inf + StdDevSampTest( + "core_nan_present", + values=[1, 2, FLOAT_NAN], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN when a NaN is present", + ), + StdDevSampTest( + "core_nan_decimal_present", + values=[1, 2, DECIMAL128_NAN], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN when a Decimal NaN value is present", + ), + StdDevSampTest( + "core_negative_nan_present", + values=[1, 2, DECIMAL128_NEGATIVE_NAN], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should still return NaN when negative NaN is present", + ), + StdDevSampTest( + "core_nan_pair", + values=[FLOAT_NAN, FLOAT_NAN], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN even for NaN pairs", + ), + StdDevSampTest( + "core_single_value_nan", + values=[FLOAT_NAN], + expected=None, + msg="Should return None when single value", + ), +] + + +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_CORE_TESTS)) +def test_stdDevSamp_core_from_list(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression core properties from a literal argument list.""" + + result = execute_expression(collection, {"$stdDevSamp": test_case.values}) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) + + +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_CORE_TESTS)) +def test_stdDevSamp_core_from_field(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression core properties from an inserted array field.""" + result = execute_expression_with_insert( + collection, {"$stdDevSamp": "$values"}, {"values": test_case.values} + ) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) From 7531837646507c776a39a62a3d17a8648349b055 Mon Sep 17 00:00:00 2001 From: Jay Date: Sun, 21 Jun 2026 16:51:28 -0400 Subject: [PATCH 03/13] Add $stdDevSamp infinity tests Signed-off-by: Jay --- .../test_expression_stdDevSamp_core.py | 2 +- .../test_expression_stdDevSamp_inf.py | 153 ++++++++++++++++++ 2 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_inf.py diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py index 1276fca79..238413280 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py @@ -110,7 +110,7 @@ class StdDevSampTest(BaseTestCase): expected=0.0, msg="Should return 0.0 when there's no variance", ), - # NaN & Inf + # NaN StdDevSampTest( "core_nan_present", values=[1, 2, FLOAT_NAN], diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_inf.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_inf.py new file mode 100644 index 000000000..78f1df7f1 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_inf.py @@ -0,0 +1,153 @@ +import math +from dataclasses import dataclass +from typing import Any + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_case import BaseTestCase +from documentdb_tests.framework.test_constants import ( + DECIMAL128_INFINITY, + DECIMAL128_NEGATIVE_INFINITY, + FLOAT_INFINITY, + FLOAT_NEGATIVE_INFINITY, +) + + +@dataclass(frozen=True) +class StdDevSampTest(BaseTestCase): + values: Any = None + + +STDDEVSAMP_INFINITY_TESTS: list[StdDevSampTest] = [ + StdDevSampTest( + "inf_scalar", + values=FLOAT_INFINITY, + expected=None, + msg="Should return None for a double infinity scalar", + ), + StdDevSampTest( + "negative_inf_scalar", + values=FLOAT_NEGATIVE_INFINITY, + expected=None, + msg="Should return None for a negative double infinity scalar", + ), + StdDevSampTest( + "decimal_inf_scalar", + values=DECIMAL128_INFINITY, + expected=None, + msg="Should return None for a decimal infinity scalar", + ), + StdDevSampTest( + "negative_decimal_inf_scalar", + values=DECIMAL128_NEGATIVE_INFINITY, + expected=None, + msg="Should return None for a negative decimal infinity scalar", + ), + StdDevSampTest( + "inf_single_array", + values=[FLOAT_INFINITY], + expected=None, + msg="Should return None for a double infinity single element array", + ), + StdDevSampTest( + "negative_inf_single_array", + values=[FLOAT_NEGATIVE_INFINITY], + expected=None, + msg="Should return None for a negative double infinity single element array", + ), + StdDevSampTest( + "decimal_inf_single_array", + values=[DECIMAL128_INFINITY], + expected=None, + msg="Should return None for a decimal infinity single element array", + ), + StdDevSampTest( + "negative_decimal_inf_single_array", + values=[DECIMAL128_NEGATIVE_INFINITY], + expected=None, + msg="Should return None for a negative decimal infinity single element array", + ), + StdDevSampTest( + "inf_with_finite", + values=[1, 2, 3, FLOAT_INFINITY], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN if a double infinity is present", + ), + StdDevSampTest( + "negative_inf_with_finite", + values=[1, 2, 3, FLOAT_NEGATIVE_INFINITY], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN if a negative double infinity is present", + ), + StdDevSampTest( + "decimal_inf_with_finite", + values=[1, 2, 3, DECIMAL128_INFINITY], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN if a decimal infinity is present", + ), + StdDevSampTest( + "negative_decimal_inf_with_finite", + values=[1, 2, 3, DECIMAL128_NEGATIVE_INFINITY], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN if a negative decimal infinity is present", + ), + StdDevSampTest( + "inf_same_sign_pair", + values=[FLOAT_INFINITY, FLOAT_INFINITY], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN for a double infinity pair of same signs", + ), + StdDevSampTest( + "inf_opp_sign_pair", + values=[FLOAT_INFINITY, FLOAT_NEGATIVE_INFINITY], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN for a double infinity pair of opposite signs", + ), + StdDevSampTest( + "decimal_inf_same_sign_pair", + values=[DECIMAL128_INFINITY, DECIMAL128_INFINITY], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN for a decimal infinity pair of same signs", + ), + StdDevSampTest( + "decimal_inf_opp_sign_pair", + values=[DECIMAL128_INFINITY, DECIMAL128_NEGATIVE_INFINITY], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN for a decimal infinity pair of opposite signs", + ), +] + + +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_INFINITY_TESTS)) +def test_stdDevSamp_infinity_from_list(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression infinity properties from a literal argument list.""" + + result = execute_expression(collection, {"$stdDevSamp": test_case.values}) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) + + +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_INFINITY_TESTS)) +def test_stdDevSamp_infinity_from_field(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression infinity properties from an inserted array field.""" + result = execute_expression_with_insert( + collection, {"$stdDevSamp": "$values"}, {"values": test_case.values} + ) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) From af0badfdfec0d80e31948dfd6673b92b3e9260e2 Mon Sep 17 00:00:00 2001 From: Jay Date: Sun, 21 Jun 2026 19:46:12 -0400 Subject: [PATCH 04/13] Add $stdDevSamp non-numeric tests Signed-off-by: Jay --- .../test_expression_stdDevSamp_non_numeric.py | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py new file mode 100644 index 000000000..2c41aa45e --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py @@ -0,0 +1,103 @@ +from dataclasses import dataclass +from datetime import datetime +from typing import Any + +import pytest +from bson import ObjectId, Regex + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_case import BaseTestCase + + +@dataclass(frozen=True) +class StdDevSampTest(BaseTestCase): + values: Any = None + + +STDDEVSAMP_NON_NUMERIC_TESTS: list[StdDevSampTest] = [ + # sole non-numerics are treated as scalar => None output + StdDevSampTest( + "sole_string", + values="string", + expected=None, + msg="Should return None for any scalar string", + ), + StdDevSampTest( + "sole_numeric_string", + values="30", + expected=None, + msg="Should return None for any scalar numeric string", + ), + StdDevSampTest( + "scalar_boolean", + values=True, + expected=None, + msg="Should return None for a boolean scalar", + ), + # non-numeric only arrays return None + StdDevSampTest( + "array_numeric_string", + values=["30"], + expected=None, + msg="Should return None for one element array with string", + ), + StdDevSampTest( + "array_all_non_numerics", + values=["3", True, Regex("a"), datetime(2026, 6, 1), ObjectId("000000000000000000000000")], + expected=None, + msg="Should return None for arrays with only non-numerics", + ), + # non-numerics are ignored among numerics + StdDevSampTest( + "numeric_string_with_one_int", + values=[60, "30"], + expected=None, + msg="Should return None due to N>1, all strings are ignored", + ), + StdDevSampTest( + "numeric_string_with_two_int", + values=[60, "30", 50], + expected=pytest.approx(7.0710678118654755), + msg="Should return stdDevSamp of all numerical values, strings are ignored", + ), + StdDevSampTest( + "non_numerics_with_two_int", + values=[60, "3", 50, True, datetime(2026, 6, 1), ObjectId("000000000000000000000000")], + expected=pytest.approx(7.0710678118654755), + msg="Should return stdDevSamp of numerical values, non-numericals are ignored", + ), +] + + +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_NON_NUMERIC_TESTS)) +def test_stdDevSamp_non_numeric_from_list(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression non numeric properties from a literal argument list.""" + + result = execute_expression(collection, {"$stdDevSamp": test_case.values}) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) + + +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_NON_NUMERIC_TESTS)) +def test_stdDevSamp_non_numeric_from_field(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression non numeric properties from an inserted array field.""" + result = execute_expression_with_insert( + collection, {"$stdDevSamp": "$values"}, {"values": test_case.values} + ) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) From 197a1d34b1363c0bab41fc7e769972b6539cfae5 Mon Sep 17 00:00:00 2001 From: Jay Date: Sun, 21 Jun 2026 20:41:15 -0400 Subject: [PATCH 05/13] Reorganize NaN and infinity tests into special value tests Signed-off-by: Jay --- .../test_expression_stdDevSamp_core.py | 35 ------------- ...t_expression_stdDevSamp_special_values.py} | 51 ++++++++++++++++--- 2 files changed, 45 insertions(+), 41 deletions(-) rename documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/{test_expression_stdDevSamp_inf.py => test_expression_stdDevSamp_special_values.py} (73%) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py index 238413280..546f35d25 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py @@ -1,4 +1,3 @@ -import math from dataclasses import dataclass from typing import Any @@ -13,10 +12,7 @@ from documentdb_tests.framework.parametrize import pytest_params from documentdb_tests.framework.test_case import BaseTestCase from documentdb_tests.framework.test_constants import ( - DECIMAL128_NAN, - DECIMAL128_NEGATIVE_NAN, DOUBLE_NEGATIVE_ZERO, - FLOAT_NAN, ) @@ -110,37 +106,6 @@ class StdDevSampTest(BaseTestCase): expected=0.0, msg="Should return 0.0 when there's no variance", ), - # NaN - StdDevSampTest( - "core_nan_present", - values=[1, 2, FLOAT_NAN], - expected=pytest.approx(math.nan, nan_ok=True), - msg="Should return NaN when a NaN is present", - ), - StdDevSampTest( - "core_nan_decimal_present", - values=[1, 2, DECIMAL128_NAN], - expected=pytest.approx(math.nan, nan_ok=True), - msg="Should return NaN when a Decimal NaN value is present", - ), - StdDevSampTest( - "core_negative_nan_present", - values=[1, 2, DECIMAL128_NEGATIVE_NAN], - expected=pytest.approx(math.nan, nan_ok=True), - msg="Should still return NaN when negative NaN is present", - ), - StdDevSampTest( - "core_nan_pair", - values=[FLOAT_NAN, FLOAT_NAN], - expected=pytest.approx(math.nan, nan_ok=True), - msg="Should return NaN even for NaN pairs", - ), - StdDevSampTest( - "core_single_value_nan", - values=[FLOAT_NAN], - expected=None, - msg="Should return None when single value", - ), ] diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_inf.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_special_values.py similarity index 73% rename from documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_inf.py rename to documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_special_values.py index 78f1df7f1..c8c005aa1 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_inf.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_special_values.py @@ -13,8 +13,11 @@ from documentdb_tests.framework.test_case import BaseTestCase from documentdb_tests.framework.test_constants import ( DECIMAL128_INFINITY, + DECIMAL128_NAN, DECIMAL128_NEGATIVE_INFINITY, + DECIMAL128_NEGATIVE_NAN, FLOAT_INFINITY, + FLOAT_NAN, FLOAT_NEGATIVE_INFINITY, ) @@ -124,9 +127,45 @@ class StdDevSampTest(BaseTestCase): ] -@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_INFINITY_TESTS)) -def test_stdDevSamp_infinity_from_list(collection, test_case: StdDevSampTest): - """Test $stdDevSamp expression infinity properties from a literal argument list.""" +STDDEVSAMP_NAN_TESTS: list[StdDevSampTest] = [ + StdDevSampTest( + "core_nan_present", + values=[1, 2, FLOAT_NAN], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN when a NaN is present", + ), + StdDevSampTest( + "core_nan_decimal_present", + values=[1, 2, DECIMAL128_NAN], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN when a Decimal NaN value is present", + ), + StdDevSampTest( + "core_negative_nan_present", + values=[1, 2, DECIMAL128_NEGATIVE_NAN], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should still return NaN when negative NaN is present", + ), + StdDevSampTest( + "core_nan_pair", + values=[FLOAT_NAN, FLOAT_NAN], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN even for NaN pairs", + ), + StdDevSampTest( + "core_single_value_nan", + values=[FLOAT_NAN], + expected=None, + msg="Should return None when single value", + ), +] + +STDDEVSAMP_SPECIAL_TESTS = STDDEVSAMP_INFINITY_TESTS + STDDEVSAMP_NAN_TESTS + + +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_SPECIAL_TESTS)) +def test_stdDevSamp_special_from_list(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression infinity & NaN properties from a literal argument list.""" result = execute_expression(collection, {"$stdDevSamp": test_case.values}) @@ -138,9 +177,9 @@ def test_stdDevSamp_infinity_from_list(collection, test_case: StdDevSampTest): ) -@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_INFINITY_TESTS)) -def test_stdDevSamp_infinity_from_field(collection, test_case: StdDevSampTest): - """Test $stdDevSamp expression infinity properties from an inserted array field.""" +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_SPECIAL_TESTS)) +def test_stdDevSamp_special_from_field(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression infinity & NaN properties from an inserted array field.""" result = execute_expression_with_insert( collection, {"$stdDevSamp": "$values"}, {"values": test_case.values} ) From c281c3cedaad4accb81334c37dca53b999e74977 Mon Sep 17 00:00:00 2001 From: Jay Date: Sat, 27 Jun 2026 23:59:58 -0400 Subject: [PATCH 06/13] Add $stdDevSamp null and missing tests Signed-off-by: Jay --- ...test_expression_stdDevSamp_null_missing.py | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py new file mode 100644 index 000000000..26b6392e9 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py @@ -0,0 +1,123 @@ +from dataclasses import dataclass +from typing import Any + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_case import BaseTestCase +from documentdb_tests.framework.test_constants import MISSING + + +@dataclass(frozen=True) +class StdDevSampTest(BaseTestCase): + values: Any = None + + +STDDEVSAMP_NULL_MISSING_TESTS: list[StdDevSampTest] = [ + StdDevSampTest( + "null_scalar", + values=None, + expected=None, + msg="Should return None for a null scalar", + ), + StdDevSampTest( + "missing_scalar", + values=MISSING, + expected=None, + msg="Should return None for a missing scalar", + ), + StdDevSampTest( + "null_single_array", + values=[None], + expected=None, + msg="Should return None for an array with single null", + ), + StdDevSampTest( + "null_first_array", + values=[None, 2, 3], + expected=0.7071067811865476, + msg="Should calculate stdDevSamp and ignore leading null", + ), + StdDevSampTest( + "null_middle_array", + values=[1, None, 3], + expected=01.4142135623730951, + msg="Should calculate stdDevSamp and ignore middle null", + ), + StdDevSampTest( + "null_end_array", + values=[1, 2, None], + expected=0.7071067811865476, + msg="Should calculate stdDevSamp and ignore ending null", + ), + StdDevSampTest( + "all_null", + values=[None, None], + expected=None, + msg="Should return None when all values are null", + ), + StdDevSampTest( + "null_leaves_one_numeric", + values=[None, 5], + expected=None, + msg="Should return None when only one numeric value with rest None", + ), + StdDevSampTest( + "missing_first_array", + values=[MISSING, 2, 3], + expected=0.7071067811865476, + msg="Should calculate stdDevSamp and ignore leading missing", + ), + StdDevSampTest( + "missing_middle_array", + values=[1, MISSING, 3], + expected=1.4142135623730951, + msg="Should calculate stdDevSamp and ignore middle missing", + ), + StdDevSampTest( + "missing_end_array", + values=[1, 2, MISSING], + expected=0.7071067811865476, + msg="Should calculate stdDevSamp and ignore ending missing", + ), + StdDevSampTest( + "missing_leaves_one_numeric", + values=[MISSING, 5], + expected=None, + msg="Should return None when only one numeric value with rest missing", + ), +] + + +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_NULL_MISSING_TESTS)) +def test_stdDevSamp_null_missing_from_list(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression null & missing properties from a literal argument list.""" + + result = execute_expression(collection, {"$stdDevSamp": test_case.values}) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) + + +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_NULL_MISSING_TESTS)) +def test_stdDevSamp_null_missing_from_field(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression null & missing properties from an inserted array field.""" + result = execute_expression_with_insert( + collection, {"$stdDevSamp": "$values"}, {"values": test_case.values} + ) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) From addd1ec57d86de927ae5699ac091d4cf36e0b99b Mon Sep 17 00:00:00 2001 From: Jay Date: Mon, 29 Jun 2026 17:30:09 -0400 Subject: [PATCH 07/13] Add test file for $stdDevSamp input form tests Signed-off-by: Jay --- .../test_expression_stdDevSamp_input_forms.py | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py new file mode 100644 index 000000000..f4f6393dd --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py @@ -0,0 +1,136 @@ +from dataclasses import dataclass +from typing import Any + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression, + execute_expression_with_insert, +) +from documentdb_tests.framework.error_codes import FAILED_TO_PARSE_ERROR, INVALID_DOLLAR_FIELD_PATH +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_case import BaseTestCase + + +@dataclass(frozen=True) +class StdDevSampTest(BaseTestCase): + values: Any = None + document: dict[str, Any] | None = None + + +STDDEVSAMP_INPUT_FORMS_TESTS: list[StdDevSampTest] = [ + # basic traversal + StdDevSampTest( + "traversal_basic", + values={"$literal": [1, 2, 3]}, + document=None, + expected=pytest.approx(1.0), + msg="Should compute stdDevSamp traversing the literal array", + ), + StdDevSampTest( + "traversal_empty_array", + values={"$literal": []}, + document=None, + expected=None, + msg="Should return null for an empty traversed array", + ), + StdDevSampTest( + "traversal_single_element", + values={"$literal": [5]}, + document=None, + expected=None, + msg="Should return null for a single traversed element", + ), + StdDevSampTest( + "nested_array", + values={"$literal": [[1, 2, 3]]}, + document=None, + expected=None, + msg="Should return null for a nested array", + ), + # expression with operand args + StdDevSampTest( + "expression_operand_add", + values=[{"$add": [1, 1]}, 4, 6], + document=None, + expected=pytest.approx(2.0), + msg="Should compute stdDevSamp dev from expression operands", + ), + StdDevSampTest( + "expression_operand_null", + values=[{"$literal": None}, 4, 6], + document=None, + expected=pytest.approx(1.4142135623730951), + msg="should calculate stdDevSamp ignoring expressions returning null", + ), + # document traversal + StdDevSampTest( + "field_refs", + values=["$a", "$b", "$c"], + document={"a": 1, "b": 2, "c": 3}, + expected=pytest.approx(1.0), + msg="Should compute stdDevSamp from multiple field references", + ), + StdDevSampTest( + "nested_object_path", + values=["$a.x", "$a.y", "$a.z"], + document={"a": {"x": 1, "y": 2, "z": 3}}, + expected=pytest.approx(1.0), + msg="Should compute stdDevSamp from nested object paths", + ), + StdDevSampTest( + "composite_array_path", + values="$a.val", + document={"a": [{"val": 1}, {"val": 5}]}, + expected=pytest.approx(2.8284271247461903), + msg="Should compute stdDevSamp from a composite array path", + ), + # field path errors + StdDevSampTest( + "fieldpath_bare_dollar", + values="$", + document=None, + error_code=INVALID_DOLLAR_FIELD_PATH, + msg="Should reject bare '$' as an invalid field path", + ), + StdDevSampTest( + "fieldpath_double_dollar", + values="$$", + document=None, + error_code=FAILED_TO_PARSE_ERROR, + msg="Should reject '$$' as an empty variable name", + ), +] + + +@pytest.mark.parametrize( + "test_case", pytest_params([t for t in STDDEVSAMP_INPUT_FORMS_TESTS if t.document is None]) +) +def test_stdDevSamp_expression(collection, test_case): + """Test $stdDevSamp expression input form expressions from a literal argument list.""" + result = execute_expression(collection, {"$stdDevSamp": test_case.values}) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) + + +@pytest.mark.parametrize( + "test_case", pytest_params([t for t in STDDEVSAMP_INPUT_FORMS_TESTS if t.document]) +) +def test_stdDevSamp_expression_from_document(collection, test_case): + """Test $stdDevSamp expression input forms form inserted document fields.""" + result = execute_expression_with_insert( + collection, {"$stdDevSamp": test_case.values}, test_case.document + ) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) From 2569520e46f67095ee9c3926e6daeded6e1018d0 Mon Sep 17 00:00:00 2001 From: Jay Date: Tue, 30 Jun 2026 16:53:54 -0400 Subject: [PATCH 08/13] Add additional fractional core tests Signed-off-by: Jay --- .../test_expression_stdDevSamp_core.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py index 546f35d25..c82a8ef7a 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_core.py @@ -54,6 +54,19 @@ class StdDevSampTest(BaseTestCase): expected=pytest.approx(1.2909944487358056), msg="Should compute sample std dev of mix numerical values", ), + # fractional + StdDevSampTest( + "core_fractional_double", + values=[1.5, 2.5], + expected=pytest.approx(0.7071067811865476), + msg="Should compute sample std dev of fractional double values", + ), + StdDevSampTest( + "core_fractional_decimal", + values=[Decimal128("1.5"), Decimal128("2.5")], + expected=pytest.approx(0.7071067811865476), + msg="Should compute sample std dev of fractional decimal values", + ), # negative operation StdDevSampTest( "core_numeric_negative", @@ -80,6 +93,13 @@ class StdDevSampTest(BaseTestCase): expected=pytest.approx(288.8194360957494), msg="Should compute sample std dev of all 1000 values", ), + # dec_high_precision_not_preserved + StdDevSampTest( + "core_high_precision", + values=[Decimal128("1.000000000000000000000000000000001"), Decimal128("1.0")], + expected=pytest.approx(0.0), + msg="Should match preservation behavior for high-precision Decimal128 inputs", + ), # N<2 -> null rule StdDevSampTest( "core_single_value", From dfbbee9381861c3fa5971f18cca9de6f4ed58b72 Mon Sep 17 00:00:00 2001 From: Jay Date: Tue, 30 Jun 2026 18:37:20 -0400 Subject: [PATCH 09/13] Add $stdDevSamp boundary tests Signed-off-by: Jay --- .../test_expression_stdDevSamp_boundaries.py | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_boundaries.py diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_boundaries.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_boundaries.py new file mode 100644 index 000000000..3cb0aa0fd --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_boundaries.py @@ -0,0 +1,129 @@ +import math +from dataclasses import dataclass +from typing import Any + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression, + execute_expression_with_insert, +) +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_case import BaseTestCase +from documentdb_tests.framework.test_constants import ( + DOUBLE_MAX, + DOUBLE_MAX_SAFE_INTEGER, + DOUBLE_MIN, + DOUBLE_MIN_SUBNORMAL, + DOUBLE_NEAR_MIN, + DOUBLE_PRECISION_LOSS, + INT32_MAX, + INT32_MAX_MINUS_1, + INT32_MIN, + INT32_MIN_PLUS_1, + INT32_OVERFLOW, + INT64_MAX, + INT64_MAX_MINUS_1, + INT64_MIN, + INT64_MIN_PLUS_1, +) + + +@dataclass(frozen=True) +class StdDevSampTest(BaseTestCase): + values: Any = None + + +STDDEVSAMP_BOUNDARIES_TESTS: list[StdDevSampTest] = [ + # boundaries + StdDevSampTest( + "bound_int32_max", + values=[INT32_MAX, INT32_MAX_MINUS_1], + expected=pytest.approx(0.7071067811865476), + msg="Should compute sample std dev for values near the int32 maximum", + ), + StdDevSampTest( + "bound_int32_min", + values=[INT32_MIN, INT32_MIN_PLUS_1], + expected=pytest.approx(0.7071067811865476), + msg="Should compute sample std dev for values near the int32 minimum", + ), + StdDevSampTest( + "bound_int64_max", + values=[INT64_MAX, INT64_MAX_MINUS_1], + expected=0.0, + msg="Should return 0.0 when int64 max values are indistinguishable at double precision", + ), + StdDevSampTest( + "bound_int64_min", + values=[INT64_MIN, INT64_MIN_PLUS_1], + expected=0.0, + msg="Should return 0.0 when int64 min values are indistinguishable at double precision", + ), + StdDevSampTest( + "bound_int32_overflow", + values=[INT32_MAX, INT32_OVERFLOW], + expected=pytest.approx(0.7071067811865476), + msg="Should handle value at int32 overflow boundary", + ), + StdDevSampTest( + "bound_double_max", + values=[DOUBLE_MAX, DOUBLE_MAX - 1], + expected=0.0, + msg="Should return 0.0 when values are indistinguishable at double maximum precision", + ), + StdDevSampTest( + "bound_double_near_0", + values=[DOUBLE_NEAR_MIN, 2e-308, 3e-308], + expected=0.0, + msg="Should return 0.0 when near-min doubles are indistinguishable at double precision", + ), + StdDevSampTest( + "bound_double_subnormal", + values=[DOUBLE_MIN_SUBNORMAL, DOUBLE_NEAR_MIN], + expected=0.0, + msg="Should return 0.0 for indistinguishable subnormal doubles at double precision", + ), + StdDevSampTest( + "bound_double_full_range", + values=[DOUBLE_MIN, DOUBLE_MAX], + expected=pytest.approx(math.nan, nan_ok=True), + msg="Should return NaN when double range spread overflows during variance computation", + ), + StdDevSampTest( + "bound_double_precision_loss", + values=[DOUBLE_MAX_SAFE_INTEGER, DOUBLE_PRECISION_LOSS], + expected=0.0, + msg="Should return 0.0 when values are indistinguishable due to double precision loss", + ), +] + + +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_BOUNDARIES_TESTS)) +def test_stdDevSamp_boundaries_from_list(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression boundaries from a literal argument list.""" + + result = execute_expression(collection, {"$stdDevSamp": test_case.values}) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) + + +@pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_BOUNDARIES_TESTS)) +def test_stdDevSamp_boundaries_from_field(collection, test_case: StdDevSampTest): + """Test $stdDevSamp expression boundaries from an inserted array field.""" + result = execute_expression_with_insert( + collection, {"$stdDevSamp": "$values"}, {"values": test_case.values} + ) + + assert_expression_result( + result, + expected=test_case.expected, + error_code=test_case.error_code, + msg=test_case.msg, + ) From 2ee2223d84c4a3743d61cf00212a031b76d66d33 Mon Sep 17 00:00:00 2001 From: Jay Date: Tue, 30 Jun 2026 18:40:52 -0400 Subject: [PATCH 10/13] Add $stdDevSamp non-numeric bool case Signed-off-by: Jay --- .../stdDevSamp/test_expression_stdDevSamp_non_numeric.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py index 2c41aa45e..1f8b1bff0 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py @@ -71,6 +71,12 @@ class StdDevSampTest(BaseTestCase): expected=pytest.approx(7.0710678118654755), msg="Should return stdDevSamp of numerical values, non-numericals are ignored", ), + StdDevSampTest( + "bool_ignored_with_numerics", + values=[True, 5, 10, False], + expected=pytest.approx(3.5355339059327378), + msg="Should ignore boolean values and compute over remaining numerics", + ), ] From c35cd0832f9976b8903051374a0b10d744208b38 Mon Sep 17 00:00:00 2001 From: Jay Date: Tue, 30 Jun 2026 18:45:40 -0400 Subject: [PATCH 11/13] Fix null tests typo + add 2 more cases Signed-off-by: Jay --- .../test_expression_stdDevSamp_null_missing.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py index 26b6392e9..934e84c20 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py @@ -46,7 +46,7 @@ class StdDevSampTest(BaseTestCase): StdDevSampTest( "null_middle_array", values=[1, None, 3], - expected=01.4142135623730951, + expected=1.4142135623730951, msg="Should calculate stdDevSamp and ignore middle null", ), StdDevSampTest( @@ -91,6 +91,18 @@ class StdDevSampTest(BaseTestCase): expected=None, msg="Should return None when only one numeric value with rest missing", ), + StdDevSampTest( + "all_missing", + values=[MISSING, MISSING], + expected=None, + msg="Should return None when all values are missing", + ), + StdDevSampTest( + "null_and_missing", + values=[None, MISSING], + expected=None, + msg="Should return None for a mix of null and missing", + ), ] From 0883bccae924ac6ae973ce296a728ef1c37e55cd Mon Sep 17 00:00:00 2001 From: Jay Rana <156965237+Jhr-4@users.noreply.github.com> Date: Tue, 30 Jun 2026 20:02:22 -0400 Subject: [PATCH 12/13] Fix typo and add explicit is `not None` check. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Jay --- .../stdDevSamp/test_expression_stdDevSamp_input_forms.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py index f4f6393dd..d30b2b886 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py @@ -120,10 +120,11 @@ def test_stdDevSamp_expression(collection, test_case): @pytest.mark.parametrize( - "test_case", pytest_params([t for t in STDDEVSAMP_INPUT_FORMS_TESTS if t.document]) + "test_case", + pytest_params([t for t in STDDEVSAMP_INPUT_FORMS_TESTS if t.document is not None]), ) def test_stdDevSamp_expression_from_document(collection, test_case): - """Test $stdDevSamp expression input forms form inserted document fields.""" + """Test $stdDevSamp expression input forms from inserted document fields.""" result = execute_expression_with_insert( collection, {"$stdDevSamp": test_case.values}, test_case.document ) From e5162cad3d8a75fe947f3921a071fe7b80dc47f5 Mon Sep 17 00:00:00 2001 From: Jay Date: Tue, 30 Jun 2026 20:41:47 -0400 Subject: [PATCH 13/13] Fix $stdDevSamp missing handling and test messages Signed-off-by: Jay --- .../test_expression_stdDevSamp_input_forms.py | 2 +- .../test_expression_stdDevSamp_non_numeric.py | 2 +- ...test_expression_stdDevSamp_null_missing.py | 43 +++---------------- 3 files changed, 8 insertions(+), 39 deletions(-) diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py index d30b2b886..02ecccc7c 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_input_forms.py @@ -62,7 +62,7 @@ class StdDevSampTest(BaseTestCase): values=[{"$literal": None}, 4, 6], document=None, expected=pytest.approx(1.4142135623730951), - msg="should calculate stdDevSamp ignoring expressions returning null", + msg="Should calculate stdDevSamp ignoring expressions returning null", ), # document traversal StdDevSampTest( diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py index 1f8b1bff0..8b0e34d18 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_non_numeric.py @@ -57,7 +57,7 @@ class StdDevSampTest(BaseTestCase): "numeric_string_with_one_int", values=[60, "30"], expected=None, - msg="Should return None due to N>1, all strings are ignored", + msg="Should return None when fewer than two numeric values remain after ignoring strings", ), StdDevSampTest( "numeric_string_with_two_int", diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py index 934e84c20..7c3cf6953 100644 --- a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/stdDevSamp/test_expression_stdDevSamp_null_missing.py @@ -67,42 +67,6 @@ class StdDevSampTest(BaseTestCase): expected=None, msg="Should return None when only one numeric value with rest None", ), - StdDevSampTest( - "missing_first_array", - values=[MISSING, 2, 3], - expected=0.7071067811865476, - msg="Should calculate stdDevSamp and ignore leading missing", - ), - StdDevSampTest( - "missing_middle_array", - values=[1, MISSING, 3], - expected=1.4142135623730951, - msg="Should calculate stdDevSamp and ignore middle missing", - ), - StdDevSampTest( - "missing_end_array", - values=[1, 2, MISSING], - expected=0.7071067811865476, - msg="Should calculate stdDevSamp and ignore ending missing", - ), - StdDevSampTest( - "missing_leaves_one_numeric", - values=[MISSING, 5], - expected=None, - msg="Should return None when only one numeric value with rest missing", - ), - StdDevSampTest( - "all_missing", - values=[MISSING, MISSING], - expected=None, - msg="Should return None when all values are missing", - ), - StdDevSampTest( - "null_and_missing", - values=[None, MISSING], - expected=None, - msg="Should return None for a mix of null and missing", - ), ] @@ -123,8 +87,13 @@ def test_stdDevSamp_null_missing_from_list(collection, test_case: StdDevSampTest @pytest.mark.parametrize("test_case", pytest_params(STDDEVSAMP_NULL_MISSING_TESTS)) def test_stdDevSamp_null_missing_from_field(collection, test_case: StdDevSampTest): """Test $stdDevSamp expression null & missing properties from an inserted array field.""" + + document = {} if test_case.values is MISSING else {"values": test_case.values} + result = execute_expression_with_insert( - collection, {"$stdDevSamp": "$values"}, {"values": test_case.values} + collection, + {"$stdDevSamp": "$values"}, + document, ) assert_expression_result(