Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""
Tests for $box valid argument handling.

Validates accepted coordinate types, argument structures,
and document location type matching behavior.
"""

import pytest
from bson import Decimal128, Int64

from documentdb_tests.compatibility.tests.core.operator.query.utils.query_test_case import (
QueryTestCase,
)
from documentdb_tests.framework.assertions import assertResult
from documentdb_tests.framework.executor import execute_command
from documentdb_tests.framework.parametrize import pytest_params

ARGUMENT_HANDLING_TESTS: list[QueryTestCase] = [
QueryTestCase(
id="int_coordinates",
filter={"loc": {"$geoWithin": {"$box": [[0, 0], [100, 100]]}}},
doc=[{"_id": 1, "loc": [50, 50]}, {"_id": 2, "loc": [150, 150]}],
expected=[{"_id": 1, "loc": [50, 50]}],
msg="$box should accept int coordinates",
),
QueryTestCase(
id="double_coordinates",
filter={"loc": {"$geoWithin": {"$box": [[0.5, 0.5], [99.5, 99.5]]}}},
doc=[{"_id": 1, "loc": [50.0, 50.0]}, {"_id": 2, "loc": [100.0, 100.0]}],
expected=[{"_id": 1, "loc": [50.0, 50.0]}],
msg="$box should accept double coordinates",
),
QueryTestCase(
id="long_coordinates",
filter={"loc": {"$geoWithin": {"$box": [[Int64(0), Int64(0)], [Int64(100), Int64(100)]]}}},
doc=[{"_id": 1, "loc": [50, 50]}, {"_id": 2, "loc": [150, 150]}],
expected=[{"_id": 1, "loc": [50, 50]}],
msg="$box should accept long coordinates",
),
QueryTestCase(
id="decimal128_coordinates",
filter={
"loc": {
"$geoWithin": {
"$box": [
[Decimal128("0"), Decimal128("0")],
[Decimal128("100"), Decimal128("100")],
]
}
}
},
doc=[{"_id": 1, "loc": [50, 50]}, {"_id": 2, "loc": [150, 150]}],
expected=[{"_id": 1, "loc": [50, 50]}],
msg="$box should accept decimal128 coordinates",
),
QueryTestCase(
id="mixed_numeric_types",
filter={"loc": {"$geoWithin": {"$box": [[0, 0.5], [Int64(100), Decimal128("100")]]}}},
doc=[{"_id": 1, "loc": [50, 50]}, {"_id": 2, "loc": [150, 150]}],
expected=[{"_id": 1, "loc": [50, 50]}],
msg="$box should accept mixed numeric types",
),
QueryTestCase(
id="three_points_accepted",
filter={"loc": {"$geoWithin": {"$box": [[0, 0], [50, 50], [100, 100]]}}},
doc=[{"_id": 1, "loc": [5, 5]}, {"_id": 2, "loc": [150, 150]}],
expected=[{"_id": 1, "loc": [5, 5]}],
msg="Three points should be accepted; only the first two define the box",
),
QueryTestCase(
id="legacy_pair_matches",
filter={"loc": {"$geoWithin": {"$box": [[0, 0], [10, 10]]}}},
doc=[{"_id": 1, "loc": [5, 5]}],
expected=[{"_id": 1, "loc": [5, 5]}],
msg="Legacy coordinate pair should match",
),
QueryTestCase(
id="object_with_xy_matches",
filter={"loc": {"$geoWithin": {"$box": [[0, 0], [10, 10]]}}},
doc=[{"_id": 1, "loc": {"x": 5, "y": 5}}],
expected=[{"_id": 1, "loc": {"x": 5, "y": 5}}],
msg="Object with x/y fields is treated as legacy coordinate pair",
),
]


@pytest.mark.parametrize("test", pytest_params(ARGUMENT_HANDLING_TESTS))
def test_box_argument_handling(collection, test):
"""Test $box argument handling — see per-case msg for details."""
collection.insert_many(test.doc)
result = execute_command(collection, {"find": collection.name, "filter": test.filter})
assertResult(result, expected=test.expected, ignore_doc_order=True, msg=test.msg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""
Tests for $box BSON type comparison.

Verifies that $box coordinate values reject invalid BSON types with expected
error codes and accept valid numeric BSON types without error.
"""

import pytest
from bson import Decimal128, Int64

from documentdb_tests.framework.assertions import assertFailureCode, assertSuccess
from documentdb_tests.framework.bson_type_validator import (
BsonType,
BsonTypeTestCase,
generate_bson_acceptance_test_cases,
generate_bson_rejection_test_cases,
)
from documentdb_tests.framework.error_codes import BAD_VALUE_ERROR
from documentdb_tests.framework.executor import execute_command

BOX_COORDINATE_PARAMS = [
BsonTypeTestCase(
id="box_coordinate",
msg="$box coordinates should reject non-numeric types",
keyword="$box",
valid_types=[BsonType.DOUBLE, BsonType.INT, BsonType.LONG, BsonType.DECIMAL],
default_error_code=BAD_VALUE_ERROR,
valid_inputs={
BsonType.DOUBLE: 10.5,
BsonType.INT: 10,
BsonType.LONG: Int64(10),
BsonType.DECIMAL: Decimal128("10"),
},
),
]

REJECTION_CASES = generate_bson_rejection_test_cases(BOX_COORDINATE_PARAMS)


@pytest.mark.parametrize(
"bson_type,sample_value,spec",
REJECTION_CASES,
)
def test_box_bson_type_rejected(collection, bson_type, sample_value, spec):
"""Test $box rejects invalid BSON types as coordinate values."""
query = {"loc": {"$geoWithin": {"$box": [[sample_value, sample_value], [10, 10]]}}}
result = execute_command(collection, {"find": collection.name, "filter": query})
assertFailureCode(result, spec.expected_code(bson_type), msg=spec.msg)


ACCEPTANCE_CASES = generate_bson_acceptance_test_cases(BOX_COORDINATE_PARAMS)


@pytest.mark.parametrize(
"bson_type,sample_value,spec",
ACCEPTANCE_CASES,
)
def test_box_bson_type_accepted(collection, bson_type, sample_value, spec):
"""Test $box accepts valid numeric BSON types as coordinate values."""
collection.insert_many(
[
{"_id": 1, "loc": [5, 5]},
{"_id": 2, "loc": [-5, -5]},
]
)
query = {"loc": {"$geoWithin": {"$box": [[0, 0], [sample_value, sample_value]]}}}
result = execute_command(collection, {"find": collection.name, "filter": query})
assertSuccess(result, [{"_id": 1, "loc": [5, 5]}], ignore_doc_order=True)
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"""
Tests for $box core functionality.

Validates basic $box behavior with $geoWithin, GeoJSON exclusion,
multiple documents, coordinate ordering, and result correctness.
"""

import pytest

from documentdb_tests.compatibility.tests.core.operator.query.utils.query_test_case import (
QueryTestCase,
)
from documentdb_tests.framework.assertions import assertResult
from documentdb_tests.framework.executor import execute_command
from documentdb_tests.framework.parametrize import pytest_params

CORE_TESTS: list[QueryTestCase] = [
QueryTestCase(
id="basic_box_returns_points_inside",
filter={"loc": {"$geoWithin": {"$box": [[0, 0], [10, 10]]}}},
doc=[
{"_id": 1, "loc": [5, 5]},
{"_id": 2, "loc": [15, 15]},
],
expected=[{"_id": 1, "loc": [5, 5]}],
msg="$box should return documents within rectangle bounds",
),
QueryTestCase(
id="does_not_return_outside",
filter={"loc": {"$geoWithin": {"$box": [[0, 0], [10, 10]]}}},
doc=[
{"_id": 1, "loc": [11, 11]},
{"_id": 2, "loc": [-1, -1]},
],
expected=[],
msg="$box should NOT return documents outside the rectangle",
),
QueryTestCase(
id="longitude_first_latitude_second",
filter={"loc": {"$geoWithin": {"$box": [[-74, 40], [-73, 41]]}}},
doc=[
{"_id": 1, "loc": [-73.5, 40.5]},
{"_id": 2, "loc": [0, 0]},
],
expected=[{"_id": 1, "loc": [-73.5, 40.5]}],
msg="$box uses longitude first, latitude second ordering",
),
QueryTestCase(
id="arbitrary_grid_coordinates",
filter={"loc": {"$geoWithin": {"$box": [[100, 200], [300, 400]]}}},
doc=[
{"_id": 1, "loc": [150, 250]},
{"_id": 2, "loc": [50, 50]},
],
expected=[{"_id": 1, "loc": [150, 250]}],
msg="$box works with arbitrary grid coordinates",
),
QueryTestCase(
id="grid_count_4x4",
filter={"loc": {"$geoWithin": {"$box": [[2, 2], [5, 5]]}}},
doc=[{"_id": i * 10 + j, "loc": [i, j]} for i in range(10) for j in range(10)],
expected=[{"_id": i * 10 + j, "loc": [i, j]} for i in range(2, 6) for j in range(2, 6)],
msg="Grid count should be 16",
),
QueryTestCase(
id="full_coverage",
filter={"loc": {"$geoWithin": {"$box": [[0, 0], [9, 9]]}}},
doc=[{"_id": i * 10 + j, "loc": [i, j]} for i in range(10) for j in range(10)],
expected=[{"_id": i * 10 + j, "loc": [i, j]} for i in range(10) for j in range(10)],
msg="Should return all 100 documents",
),
QueryTestCase(
id="geojson_point_within_bounds_matched",
filter={"loc": {"$geoWithin": {"$box": [[0, 0], [10, 10]]}}},
doc=[{"_id": 1, "loc": {"type": "Point", "coordinates": [5, 5]}}],
expected=[{"_id": 1, "loc": {"type": "Point", "coordinates": [5, 5]}}],
msg="$box matches documents stored as GeoJSON Point within bounds",
),
QueryTestCase(
id="multiple_inside_all_returned",
filter={"loc": {"$geoWithin": {"$box": [[0, 0], [10, 10]]}}},
doc=[
{"_id": 1, "loc": [1, 1]},
{"_id": 2, "loc": [5, 5]},
{"_id": 3, "loc": [9, 9]},
],
expected=[
{"_id": 1, "loc": [1, 1]},
{"_id": 2, "loc": [5, 5]},
{"_id": 3, "loc": [9, 9]},
],
msg="$box should return all documents inside the rectangle",
),
QueryTestCase(
id="mixed_inside_outside",
filter={"loc": {"$geoWithin": {"$box": [[0, 0], [10, 10]]}}},
doc=[
{"_id": 1, "loc": [5, 5]},
{"_id": 2, "loc": [15, 15]},
{"_id": 3, "loc": [8, 8]},
],
expected=[
{"_id": 1, "loc": [5, 5]},
{"_id": 3, "loc": [8, 8]},
],
msg="$box should return only documents inside the rectangle",
),
QueryTestCase(
id="no_documents_inside",
filter={"loc": {"$geoWithin": {"$box": [[0, 0], [10, 10]]}}},
doc=[
{"_id": 1, "loc": [20, 20]},
{"_id": 2, "loc": [30, 30]},
],
expected=[],
msg="$box should return empty result when no documents are inside",
),
]


@pytest.mark.parametrize("test", pytest_params(CORE_TESTS))
def test_box_core(collection, test):
"""Test $box core functionality with $geoWithin."""
collection.insert_many(test.doc)
result = execute_command(collection, {"find": collection.name, "filter": test.filter})
assertResult(result, expected=test.expected, ignore_doc_order=True, msg=test.msg)


def test_box_empty_collection(collection):
"""Test $box returns empty result on empty collection."""
result = execute_command(
collection,
{"find": collection.name, "filter": {"loc": {"$geoWithin": {"$box": [[0, 0], [10, 10]]}}}},
)
assertResult(result, expected=[], msg="$box should return empty result on empty collection")
Loading
Loading