diff --git a/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_core.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_core.py new file mode 100644 index 000000000..40dd8f381 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_core.py @@ -0,0 +1,110 @@ +"""Core selection semantics for the $percentile expression operator. + +With ``method: "approximate"`` (a t-digest), small inputs return the actual data +point at rank ceil(p*n). Results are returned as an array, one value per ``p``, +in the same order as ``p``. Captured against MongoDB 8.3.4. +""" + +from __future__ import annotations + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.accumulator.percentile.utils.percentile_common import ( # noqa: E501 + PercentileTest, + percentile_spec, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression, +) +from documentdb_tests.framework.parametrize import pytest_params + +# Property [Percentile Selection]: $percentile returns the value at the rank +# corresponding to each probability, selecting an order statistic of the input. +PERCENTILE_SELECTION_TESTS: list[PercentileTest] = [ + PercentileTest( + "core_median", + spec=percentile_spec([10, 20, 30], [0.5]), + expected=[20.0], + msg="$percentile p=[0.5] over [10,20,30] should return the median [20.0]", + ), + PercentileTest( + "core_min_p0", + spec=percentile_spec([10, 20, 30], [0.0]), + expected=[10.0], + msg="$percentile p=[0.0] should return the minimum [10.0]", + ), + PercentileTest( + "core_max_p1", + spec=percentile_spec([10, 20, 30], [1.0]), + expected=[30.0], + msg="$percentile p=[1.0] should return the maximum [30.0]", + ), + PercentileTest( + "core_single_element", + spec=percentile_spec([42], [0.5]), + expected=[42.0], + msg="$percentile over a single-element array should return that element", + ), + PercentileTest( + "core_unsorted_input", + spec=percentile_spec([30, 10, 20], [0.5]), + expected=[20.0], + msg="$percentile should sort input internally; median of [30,10,20] is [20.0]", + ), + PercentileTest( + "core_all_equal", + spec=percentile_spec([7, 7, 7], [0.5]), + expected=[7.0], + msg="$percentile over all-equal values should return that value", + ), + PercentileTest( + "core_large_input", + spec=percentile_spec(list(range(10_000)), [0.5]), + expected=[4999.0], + msg="$percentile should handle a large (10000-element) input", + ), +] + +# Property [P Ordering]: results follow the order of the ``p`` array, including +# descending and duplicate probabilities. +PERCENTILE_ORDERING_TESTS: list[PercentileTest] = [ + PercentileTest( + "order_multiple_ascending", + spec=percentile_spec([10, 20, 30, 40, 50], [0.25, 0.5, 0.95]), + expected=[20.0, 30.0, 50.0], + msg="$percentile with multiple ascending p values should return results in p order", + ), + PercentileTest( + "order_descending_p", + spec=percentile_spec([10, 20, 30, 40, 50], [0.95, 0.05]), + expected=[50.0, 10.0], + msg="$percentile should preserve descending p order in the output", + ), + PercentileTest( + "order_duplicate_p", + spec=percentile_spec([10, 20, 30], [0.5, 0.5]), + expected=[20.0, 20.0], + msg="$percentile with duplicate p values should return a result for each", + ), + PercentileTest( + "order_boundaries_both", + spec=percentile_spec([10, 20, 30], [0.0, 1.0]), + expected=[10.0, 30.0], + msg="$percentile with p=[0.0, 1.0] should return [min, max]", + ), +] + +PERCENTILE_CORE_ALL_TESTS = PERCENTILE_SELECTION_TESTS + PERCENTILE_ORDERING_TESTS + + +@pytest.mark.parametrize("test_case", pytest_params(PERCENTILE_CORE_ALL_TESTS)) +def test_percentile_core(collection, test_case: PercentileTest): + """Test $percentile core selection semantics.""" + result = execute_expression(collection, {"$percentile": test_case.spec}) + 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/accumulator/percentile/test_percentile_data_types.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_data_types.py new file mode 100644 index 000000000..3d7d4c731 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_data_types.py @@ -0,0 +1,99 @@ +"""Numeric data-type and special-value coverage for $percentile. + +The ``approximate`` method computes in double precision and always returns +double values, regardless of input numeric type. Captured against MongoDB 8.3.4. +""" + +from __future__ import annotations + +import pytest +from bson import Decimal128, Int64 + +from documentdb_tests.compatibility.tests.core.operator.expressions.accumulator.percentile.utils.percentile_common import ( # noqa: E501 + PercentileTest, + percentile_spec, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression, +) +from documentdb_tests.framework.parametrize import pytest_params +from documentdb_tests.framework.test_constants import ( + FLOAT_INFINITY, + FLOAT_NAN, + FLOAT_NEGATIVE_INFINITY, +) + +# Property [Numeric Types]: $percentile accepts all numeric BSON types and +# returns a double under the approximate method. +PERCENTILE_NUMERIC_TYPE_TESTS: list[PercentileTest] = [ + PercentileTest( + "type_int32", + spec=percentile_spec([10, 20, 30], [0.5]), + expected=[20.0], + msg="$percentile should compute over int32 input and return a double", + ), + PercentileTest( + "type_int64", + spec=percentile_spec({"$literal": [Int64(10), Int64(20), Int64(30)]}, [0.5]), + expected=[20.0], + msg="$percentile should compute over int64 input and return a double", + ), + PercentileTest( + "type_double", + spec=percentile_spec({"$literal": [10.0, 20.5, 30.0]}, [0.5]), + expected=[20.5], + msg="$percentile should compute over double input", + ), + PercentileTest( + "type_decimal128_returns_double", + spec=percentile_spec( + {"$literal": [Decimal128("10"), Decimal128("20"), Decimal128("30")]}, [0.5] + ), + expected=[20.0], + msg="$percentile over Decimal128 input should still return a double (approximate)", + ), + PercentileTest( + "type_mixed_numeric", + spec=percentile_spec({"$literal": [10, 20.5, Decimal128("30")]}, [0.5]), + expected=[20.5], + msg="$percentile should compute across mixed numeric types", + ), +] + +# Property [Special Values]: NaN and infinities participate in ordering; +# the approximate method selects an order statistic accordingly. +PERCENTILE_SPECIAL_VALUE_TESTS: list[PercentileTest] = [ + PercentileTest( + "special_nan_in_input", + spec=percentile_spec({"$literal": [10, FLOAT_NAN, 30]}, [0.5]), + expected=[10.0], + msg="$percentile should order NaN as the smallest value in the input", + ), + PercentileTest( + "special_positive_infinity", + spec=percentile_spec({"$literal": [10, FLOAT_INFINITY, 30]}, [0.5]), + expected=[30.0], + msg="$percentile should treat +Infinity as the largest value", + ), + PercentileTest( + "special_negative_infinity", + spec=percentile_spec({"$literal": [FLOAT_NEGATIVE_INFINITY, 10, 30]}, [0.5]), + expected=[10.0], + msg="$percentile should treat -Infinity as the smallest value", + ), +] + +PERCENTILE_DATA_TYPE_ALL_TESTS = PERCENTILE_NUMERIC_TYPE_TESTS + PERCENTILE_SPECIAL_VALUE_TESTS + + +@pytest.mark.parametrize("test_case", pytest_params(PERCENTILE_DATA_TYPE_ALL_TESTS)) +def test_percentile_data_types(collection, test_case: PercentileTest): + """Test $percentile numeric-type and special-value handling.""" + result = execute_expression(collection, {"$percentile": test_case.spec}) + 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/accumulator/percentile/test_percentile_input_forms.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_input_forms.py new file mode 100644 index 000000000..3104a9142 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_input_forms.py @@ -0,0 +1,86 @@ +"""Input-form coverage for the $percentile expression operator. + +Verifies that ``input`` accepts each expression form (literal array, raw array +expression, scalar, expression operator, field reference, dotted path) and that +an empty input yields [null]. Captured against MongoDB 8.3.4. +""" + +from __future__ import annotations + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.accumulator.percentile.utils.percentile_common import ( # noqa: E501 + PercentileTest, + percentile_spec, +) +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 + +# Property [Input Forms]: ``input`` accepts any expression that resolves to a +# number or an array of numbers. +PERCENTILE_INPUT_FORMS_TESTS: list[PercentileTest] = [ + PercentileTest( + "form_literal_array", + spec=percentile_spec({"$literal": [10, 20, 30]}, [0.5]), + expected=[20.0], + msg="$percentile should accept a $literal array as input", + ), + PercentileTest( + "form_raw_array", + spec=percentile_spec([10, 20, 30], [0.5]), + expected=[20.0], + msg="$percentile should accept a raw array expression as input", + ), + PercentileTest( + "form_scalar_number", + spec=percentile_spec(42, [0.5]), + expected=[42.0], + msg="$percentile should accept a single scalar number as input", + ), + PercentileTest( + "form_expression_operator", + spec=percentile_spec({"$concatArrays": [[10, 20], [30]]}, [0.5]), + expected=[20.0], + msg="$percentile should accept an expression operator that resolves to an array", + ), + PercentileTest( + "form_empty_array", + spec=percentile_spec([], [0.5]), + expected=[None], + msg="$percentile over an empty array input should return [null]", + ), + PercentileTest( + "form_field_array", + spec=percentile_spec("$v", [0.5]), + document={"v": [10, 20, 30]}, + expected=[20.0], + msg="$percentile should read an array from a field reference", + ), + PercentileTest( + "form_dotted_path", + spec=percentile_spec("$a.b", [0.5]), + document={"a": [{"b": 1}, {"b": 2}, {"b": 3}]}, + expected=[2.0], + msg="$percentile should resolve a dotted path over an array of objects", + ), +] + + +@pytest.mark.parametrize("test_case", pytest_params(PERCENTILE_INPUT_FORMS_TESTS)) +def test_percentile_input_form(collection, test_case: PercentileTest): + """Test $percentile input forms.""" + expr = {"$percentile": test_case.spec} + if test_case.document is not None: + result = execute_expression_with_insert(collection, expr, test_case.document) + else: + result = execute_expression(collection, expr) + 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/accumulator/percentile/test_percentile_method.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_method.py new file mode 100644 index 000000000..b1aad9e2d --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_method.py @@ -0,0 +1,105 @@ +"""Validation of ``method`` and the spec document for $percentile. + +Only ``method: "approximate"`` is supported (MongoDB 8.x rejects "discrete" +and "continuous"). The spec must be an object containing the required fields +``input``, ``p``, and ``method``. Captured against MongoDB 8.3.4. +""" + +from __future__ import annotations + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.accumulator.percentile.utils.percentile_common import ( # noqa: E501 + PercentileTest, + percentile_spec, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression, +) +from documentdb_tests.framework.error_codes import ( + BAD_VALUE_ERROR, + MISSING_FIELD_ERROR, + PERCENTILE_SPEC_NOT_OBJECT_ERROR, + TYPE_MISMATCH_ERROR, + UNRECOGNIZED_COMMAND_FIELD_ERROR, +) +from documentdb_tests.framework.parametrize import pytest_params + +# Property [Method Validation]: only "approximate" is accepted; other strings +# are rejected, and a non-string method is a type error. +PERCENTILE_METHOD_TESTS: list[PercentileTest] = [ + PercentileTest( + "method_discrete_unsupported", + spec=percentile_spec([10, 20, 30], [0.5], "discrete"), + error_code=BAD_VALUE_ERROR, + msg="$percentile method 'discrete' is unsupported and should fail with BadValue", + ), + PercentileTest( + "method_continuous_unsupported", + spec=percentile_spec([10, 20, 30], [0.5], "continuous"), + error_code=BAD_VALUE_ERROR, + msg="$percentile method 'continuous' is unsupported and should fail with BadValue", + ), + PercentileTest( + "method_invalid_string", + spec=percentile_spec([10, 20, 30], [0.5], "exact"), + error_code=BAD_VALUE_ERROR, + msg="$percentile with an unknown method string should fail with BadValue", + ), + PercentileTest( + "method_wrong_type", + spec=percentile_spec([10, 20, 30], [0.5], 5), + error_code=TYPE_MISMATCH_ERROR, + msg="$percentile with a non-string method should fail with TypeMismatch", + ), +] + +# Property [Spec Validation]: the spec must be an object with the required +# fields; missing fields, unknown fields, and non-object specs are rejected. +PERCENTILE_SPEC_TESTS: list[PercentileTest] = [ + PercentileTest( + "spec_missing_method", + spec={"input": [10, 20, 30], "p": [0.5]}, + error_code=MISSING_FIELD_ERROR, + msg="$percentile without method should fail with a missing-field error", + ), + PercentileTest( + "spec_missing_input", + spec={"p": [0.5], "method": "approximate"}, + error_code=MISSING_FIELD_ERROR, + msg="$percentile without input should fail with a missing-field error", + ), + PercentileTest( + "spec_missing_p", + spec={"input": [10, 20, 30], "method": "approximate"}, + error_code=MISSING_FIELD_ERROR, + msg="$percentile without p should fail with a missing-field error", + ), + PercentileTest( + "spec_unknown_field", + spec={"input": [10, 20, 30], "p": [0.5], "method": "approximate", "foo": 1}, + error_code=UNRECOGNIZED_COMMAND_FIELD_ERROR, + msg="$percentile with an unknown spec field should fail with an unknown-field error", + ), + PercentileTest( + "spec_not_object", + spec=[1, 2, 3], + error_code=PERCENTILE_SPEC_NOT_OBJECT_ERROR, + msg="$percentile with a non-object spec should fail with a spec-not-object error", + ), +] + +PERCENTILE_METHOD_ALL_TESTS = PERCENTILE_METHOD_TESTS + PERCENTILE_SPEC_TESTS + + +@pytest.mark.parametrize("test_case", pytest_params(PERCENTILE_METHOD_ALL_TESTS)) +def test_percentile_method(collection, test_case: PercentileTest): + """Test $percentile method and spec-document validation.""" + result = execute_expression(collection, {"$percentile": test_case.spec}) + 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/accumulator/percentile/test_percentile_non_numeric.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_non_numeric.py new file mode 100644 index 000000000..9f296c953 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_non_numeric.py @@ -0,0 +1,54 @@ +"""Non-numeric input handling for the $percentile expression operator. + +$percentile considers only numeric values, ignoring non-numeric entries, and +returns [null] when no numeric value remains. Captured against MongoDB 8.3.4. +""" + +from __future__ import annotations + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.accumulator.percentile.utils.percentile_common import ( # noqa: E501 + PercentileTest, + percentile_spec, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression, +) +from documentdb_tests.framework.parametrize import pytest_params + +# Property [Non-Numeric Handling]: non-numeric entries are excluded; an input +# with no numeric values yields [null]. +PERCENTILE_NON_NUMERIC_TESTS: list[PercentileTest] = [ + PercentileTest( + "nonnum_ignored", + spec=percentile_spec({"$literal": [10, "x", 20, True, 30]}, [0.5]), + expected=[20.0], + msg="$percentile should ignore non-numeric entries and use the numeric set [10,20,30]", + ), + PercentileTest( + "nonnum_all_non_numeric", + spec=percentile_spec({"$literal": ["a", "b"]}, [0.5]), + expected=[None], + msg="$percentile over all-non-numeric input should return [null]", + ), + PercentileTest( + "nonnum_all_null", + spec=percentile_spec({"$literal": [None, None]}, [0.5]), + expected=[None], + msg="$percentile over all-null input should return [null]", + ), +] + + +@pytest.mark.parametrize("test_case", pytest_params(PERCENTILE_NON_NUMERIC_TESTS)) +def test_percentile_non_numeric(collection, test_case: PercentileTest): + """Test $percentile non-numeric input handling.""" + result = execute_expression(collection, {"$percentile": test_case.spec}) + 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/accumulator/percentile/test_percentile_null.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_null.py new file mode 100644 index 000000000..4e75bfabc --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_null.py @@ -0,0 +1,59 @@ +"""Null and missing input handling for the $percentile expression operator. + +A null or missing ``input`` yields one null per requested percentile. +Captured against MongoDB 8.3.4. +""" + +from __future__ import annotations + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.accumulator.percentile.utils.percentile_common import ( # noqa: E501 + PercentileTest, + percentile_spec, +) +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 + +# Property [Null/Missing Input]: null or missing input produces [null], one per p. +PERCENTILE_NULL_TESTS: list[PercentileTest] = [ + PercentileTest( + "null_input", + spec=percentile_spec({"$literal": None}, [0.5]), + expected=[None], + msg="$percentile over a null input should return [null]", + ), + PercentileTest( + "null_input_multiple_p", + spec=percentile_spec({"$literal": None}, [0.25, 0.75]), + expected=[None, None], + msg="$percentile over a null input should return one null per requested p", + ), + PercentileTest( + "missing_input_field", + spec=percentile_spec("$v", [0.5]), + document={"x": 1}, + expected=[None], + msg="$percentile over a missing input field should return [null]", + ), +] + + +@pytest.mark.parametrize("test_case", pytest_params(PERCENTILE_NULL_TESTS)) +def test_percentile_null(collection, test_case: PercentileTest): + """Test $percentile null and missing input handling.""" + expr = {"$percentile": test_case.spec} + if test_case.document is not None: + result = execute_expression_with_insert(collection, expr, test_case.document) + else: + result = execute_expression(collection, expr) + 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/accumulator/percentile/test_percentile_p_validation.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_p_validation.py new file mode 100644 index 000000000..6bdfbbf6f --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/test_percentile_p_validation.py @@ -0,0 +1,89 @@ +"""Validation of the ``p`` field for the $percentile expression operator. + +``p`` must be an array of numbers in [0.0, 1.0]. Captured against MongoDB 8.3.4. +""" + +from __future__ import annotations + +import pytest + +from documentdb_tests.compatibility.tests.core.operator.expressions.accumulator.percentile.utils.percentile_common import ( # noqa: E501 + PercentileTest, + percentile_spec, +) +from documentdb_tests.compatibility.tests.core.operator.expressions.utils.utils import ( + assert_expression_result, + execute_expression, +) +from documentdb_tests.framework.error_codes import ( + PERCENTILE_INVALID_P_FIELD_ERROR, + PERCENTILE_INVALID_P_TYPE_ERROR, + PERCENTILE_INVALID_P_VALUE_ERROR, +) +from documentdb_tests.framework.parametrize import pytest_params + +# Property [P Range]: probabilities outside [0.0, 1.0] are rejected; the +# boundaries themselves are valid. +PERCENTILE_P_RANGE_TESTS: list[PercentileTest] = [ + PercentileTest( + "p_above_one", + spec=percentile_spec([10, 20, 30], [1.5]), + error_code=PERCENTILE_INVALID_P_VALUE_ERROR, + msg="$percentile with p > 1 should fail with INVALID_P_VALUE", + ), + PercentileTest( + "p_below_zero", + spec=percentile_spec([10, 20, 30], [-0.1]), + error_code=PERCENTILE_INVALID_P_VALUE_ERROR, + msg="$percentile with p < 0 should fail with INVALID_P_VALUE", + ), + PercentileTest( + "p_boundary_zero", + spec=percentile_spec([10, 20, 30], [0.0]), + expected=[10.0], + msg="$percentile with p=0.0 is valid and returns the minimum", + ), + PercentileTest( + "p_boundary_one", + spec=percentile_spec([10, 20, 30], [1.0]), + expected=[30.0], + msg="$percentile with p=1.0 is valid and returns the maximum", + ), +] + +# Property [P Field Shape]: ``p`` must be a non-empty array; non-array and empty +# values are rejected, and non-numeric elements are rejected. +PERCENTILE_P_SHAPE_TESTS: list[PercentileTest] = [ + PercentileTest( + "p_scalar_not_array", + spec={"input": [10, 20, 30], "p": 0.5, "method": "approximate"}, + error_code=PERCENTILE_INVALID_P_FIELD_ERROR, + msg="$percentile with a scalar (non-array) p should fail with INVALID_P_FIELD", + ), + PercentileTest( + "p_empty_array", + spec=percentile_spec([10, 20, 30], []), + error_code=PERCENTILE_INVALID_P_FIELD_ERROR, + msg="$percentile with an empty p array should fail with INVALID_P_FIELD", + ), + PercentileTest( + "p_non_numeric_element", + spec=percentile_spec([10, 20, 30], ["x"]), + error_code=PERCENTILE_INVALID_P_TYPE_ERROR, + msg="$percentile with a non-numeric p element should fail with INVALID_P_TYPE", + ), +] + +PERCENTILE_P_VALIDATION_ALL_TESTS = PERCENTILE_P_RANGE_TESTS + PERCENTILE_P_SHAPE_TESTS + + +@pytest.mark.parametrize("test_case", pytest_params(PERCENTILE_P_VALIDATION_ALL_TESTS)) +def test_percentile_p_validation(collection, test_case: PercentileTest): + """Test $percentile p-field validation.""" + result = execute_expression(collection, {"$percentile": test_case.spec}) + 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/accumulator/percentile/utils/percentile_common.py b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/utils/percentile_common.py new file mode 100644 index 000000000..8047fdc00 --- /dev/null +++ b/documentdb_tests/compatibility/tests/core/operator/expressions/accumulator/percentile/utils/percentile_common.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +from documentdb_tests.framework.test_case import BaseTestCase + + +@dataclass(frozen=True) +class PercentileTest(BaseTestCase): + """Test case for the $percentile expression operator.""" + + spec: Any = None # full {input, p, method} document, or a malformed variant + document: Any = None # optional doc to insert for field-reference cases + + +def percentile_spec(input, p=(0.5,), method="approximate"): + """Build a valid $percentile spec. Pass a raw dict for malformed-spec cases.""" + return {"input": input, "p": list(p), "method": method} diff --git a/documentdb_tests/framework/error_codes.py b/documentdb_tests/framework/error_codes.py index 423b3fe65..2083538f0 100644 --- a/documentdb_tests/framework/error_codes.py +++ b/documentdb_tests/framework/error_codes.py @@ -517,6 +517,7 @@ WILDCARD_STRING_TYPE_ERROR = 7246202 OUT_TIMESERIES_COLLECTION_TYPE_ERROR = 7268700 OUT_TIMESERIES_OPTIONS_MISMATCH_ERROR = 7406103 +PERCENTILE_SPEC_NOT_OBJECT_ERROR = 7436200 SORT_DUPLICATE_KEY_ERROR = 7472500 N_ACCUMULATOR_INVALID_N_ERROR = 7548606 GEO_NEAR_MIN_DISTANCE_NOT_CONSTANT_ERROR = 7555701 @@ -527,6 +528,7 @@ QUERYSETTINGS_NON_DOCUMENT_ARG_ERROR = 7746800 PIPELINE_LENGTH_LIMIT_ERROR = 7749501 PERCENTILE_INVALID_P_FIELD_ERROR = 7750301 +PERCENTILE_INVALID_P_TYPE_ERROR = 7750302 PERCENTILE_INVALID_P_VALUE_ERROR = 7750303 ENCRYPTED_FIELD_TRIM_FACTOR_OUT_OF_RANGE_ERROR = 8574000 QUERYSETTINGS_INTERNAL_DB_ERROR = 8584900