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,72 @@
"""Tests for compound index key sort order BSON type validation.

Verifies that compound index key values (sort order specifiers) reject
invalid BSON types and accept valid numeric types.
"""

import pytest
from bson import Decimal128, Int64

from documentdb_tests.framework.assertions import assertFailureCode, assertNotError
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 CANNOT_CREATE_INDEX_ERROR
from documentdb_tests.framework.executor import execute_command

pytestmark = pytest.mark.index

COMPOUND_KEY_SORT_ORDER_PARAMS = [
BsonTypeTestCase(
id="sort_order",
msg="compound key sort order should reject non-numeric types",
keyword="sort_order",
valid_types=[BsonType.DOUBLE, BsonType.INT, BsonType.LONG, BsonType.DECIMAL],
default_error_code=CANNOT_CREATE_INDEX_ERROR,
valid_inputs={
BsonType.DOUBLE: 1.0,
BsonType.INT: 1,
BsonType.LONG: Int64(1),
BsonType.DECIMAL: Decimal128("1"),
},
),
]


def _build_index(sample_value):
"""Build a createIndexes spec with sample_value as a key sort order."""
return {"key": {"a": sample_value, "b": 1}, "name": "test_idx"}


REJECTION_CASES = generate_bson_rejection_test_cases(COMPOUND_KEY_SORT_ORDER_PARAMS)


@pytest.mark.parametrize("bson_type,sample_value,spec", REJECTION_CASES)
def test_compound_key_sort_order_rejected(collection, bson_type, sample_value, spec):
"""Test compound index creation rejects invalid BSON types for key sort order."""
result = execute_command(
collection,
{"createIndexes": collection.name, "indexes": [_build_index(sample_value)]},
)
assertFailureCode(result, spec.expected_code(bson_type), msg=spec.msg)


ACCEPTANCE_CASES = generate_bson_acceptance_test_cases(COMPOUND_KEY_SORT_ORDER_PARAMS)


@pytest.mark.parametrize("bson_type,sample_value,spec", ACCEPTANCE_CASES)
def test_compound_key_sort_order_accepted(collection, bson_type, sample_value, spec):
"""Test compound index creation accepts valid BSON types for key sort order.

Note: This is a type validation test, not a functional test. We only verify
the command does not error — we do not check listIndexes to confirm the index
was created with the correct option value.
"""
result = execute_command(
collection,
{"createIndexes": collection.name, "indexes": [_build_index(sample_value)]},
)
assertNotError(result, msg=f"sort order should accept {bson_type.value}")
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""Tests for compound index creation and idempotency.

Validates multi-field key creation and duplicate/ordering behavior.
"""

import pytest

from documentdb_tests.compatibility.tests.core.indexes.commands.utils.index_test_case import (
IndexTestCase,
index_created_response,
)
from documentdb_tests.framework.assertions import (
assertSuccessPartial,
)
from documentdb_tests.framework.executor import execute_command
from documentdb_tests.framework.parametrize import pytest_params

pytestmark = pytest.mark.index

CREATION_SUCCESS_TESTS: list[IndexTestCase] = [
Comment thread
vic-tsang marked this conversation as resolved.
IndexTestCase(
id="creation_mixed_sort_orders",
indexes=({"key": {"a": 1, "b": -1, "c": 1, "d": -1, "e": 1}, "name": "abcde"},),
msg="5-field compound with mixed sort orders succeeds",
),
IndexTestCase(
id="creation_dot_notation",
indexes=({"key": {"a.b": 1, "a.c": 1}, "name": "ab_ac"},),
msg="Dot notation fields succeed",
),
]


@pytest.mark.parametrize("test", pytest_params(CREATION_SUCCESS_TESTS))
def test_compound_creation_success(collection, test):
"""Test compound index creation success cases."""
result = execute_command(
collection,
{"createIndexes": collection.name, "indexes": list(test.indexes)},
)
assertSuccessPartial(result, index_created_response(), test.msg)


def test_compound_idempotent(collection):
"""Test creating same compound index twice is idempotent."""
execute_command(
collection,
{
"createIndexes": collection.name,
"indexes": [{"key": {"a": 1, "b": 1}, "name": "a_1_b_1"}],
},
)
result = execute_command(
collection,
{
"createIndexes": collection.name,
"indexes": [{"key": {"a": 1, "b": 1}, "name": "a_1_b_1"}],
},
)
assertSuccessPartial(
result, {"ok": 1.0, "numIndexesBefore": 2, "numIndexesAfter": 2}, msg="Duplicate is no-op"
)


def test_compound_different_order_creates_two(collection):
"""Test same fields different order creates two separate indexes."""
execute_command(
collection,
{
"createIndexes": collection.name,
"indexes": [{"key": {"a": 1, "b": 1}, "name": "a_1_b_1"}],
},
)
result = execute_command(
collection,
{
"createIndexes": collection.name,
"indexes": [{"key": {"b": 1, "a": 1}, "name": "b_1_a_1"}],
},
)
assertSuccessPartial(
result,
{"ok": 1.0, "numIndexesBefore": 2, "numIndexesAfter": 3},
msg="Different field order = separate index",
)


def test_compound_different_sort_creates_two(collection):
"""Test same fields different sort direction creates two indexes."""
execute_command(
collection,
{
"createIndexes": collection.name,
"indexes": [{"key": {"a": 1, "b": 1}, "name": "a_1_b_1"}],
},
)
result = execute_command(
collection,
{
"createIndexes": collection.name,
"indexes": [{"key": {"a": 1, "b": -1}, "name": "a_1_b_neg1"}],
},
)
assertSuccessPartial(
result,
{"ok": 1.0, "numIndexesBefore": 2, "numIndexesAfter": 3},
msg="Different sort = separate index",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""Tests for compound index error cases.

Validates error codes for invalid sort orders, naming conflicts,
and parallel arrays in compound indexes.
"""

import pytest

from documentdb_tests.compatibility.tests.core.indexes.commands.utils.index_test_case import (
IndexTestCase,
)
from documentdb_tests.framework.assertions import assertFailureCode, assertSuccessPartial
from documentdb_tests.framework.error_codes import (
CANNOT_CREATE_INDEX_ERROR,
CANNOT_INDEX_PARALLEL_ARRAYS_ERROR,
INDEX_KEY_SPECS_CONFLICT_ERROR,
INDEX_OPTIONS_CONFLICT_ERROR,
)
from documentdb_tests.framework.executor import execute_command
from documentdb_tests.framework.parametrize import pytest_params

pytestmark = pytest.mark.index

COMPOUND_CREATION_ERROR_TESTS: list[IndexTestCase] = [
Comment thread
vic-tsang marked this conversation as resolved.
IndexTestCase(
id="sort_order_zero",
indexes=({"key": {"a": 1, "b": 0}, "name": "bad"},),
error_code=CANNOT_CREATE_INDEX_ERROR,
msg="Sort order 0 in compound should fail",
),
IndexTestCase(
id="sort_order_null",
indexes=({"key": {"a": None, "b": 1}, "name": "bad"},),
error_code=CANNOT_CREATE_INDEX_ERROR,
msg="Sort order null in compound should fail",
),
IndexTestCase(
id="sort_order_invalid_string",
indexes=({"key": {"a": "invalid", "b": 1}, "name": "bad"},),
error_code=CANNOT_CREATE_INDEX_ERROR,
msg="Invalid string sort order in compound should fail",
),
]


@pytest.mark.parametrize("test", pytest_params(COMPOUND_CREATION_ERROR_TESTS))
def test_compound_creation_error(collection, test):
"""Test compound index creation error cases."""
result = execute_command(
collection,
{"createIndexes": collection.name, "indexes": list(test.indexes)},
)
assertFailureCode(result, test.error_code, test.msg)


COMPOUND_CONFLICT_ERROR_TESTS: list[IndexTestCase] = [
IndexTestCase(
id="conflict_same_fields_different_name",
setup_indexes=[{"key": {"a": 1, "b": 1}, "name": "idx1"}],
indexes=({"key": {"a": 1, "b": 1}, "name": "idx2"},),
error_code=INDEX_OPTIONS_CONFLICT_ERROR,
msg="Same fields/order different name should fail",
),
IndexTestCase(
id="conflict_same_name_different_fields",
setup_indexes=[{"key": {"a": 1, "b": 1}, "name": "my_idx"}],
indexes=({"key": {"c": 1, "d": 1}, "name": "my_idx"},),
error_code=INDEX_KEY_SPECS_CONFLICT_ERROR,
msg="Same name different fields should fail",
),
]


@pytest.mark.parametrize("test", pytest_params(COMPOUND_CONFLICT_ERROR_TESTS))
def test_compound_conflict_error(collection, test):
"""Test compound index naming conflict error cases."""
execute_command(
collection,
{"createIndexes": collection.name, "indexes": list(test.setup_indexes)},
)
result = execute_command(
collection,
{"createIndexes": collection.name, "indexes": list(test.indexes)},
)
assertFailureCode(result, test.error_code, test.msg)


def test_compound_parallel_arrays_fail(collection):
"""Test compound index with both fields as arrays fails (parallel arrays)."""
execute_command(
collection,
{
"createIndexes": collection.name,
"indexes": [{"key": {"tags": 1, "category": 1}, "name": "tags_cat"}],
},
)
result = execute_command(
collection,
{"insert": collection.name, "documents": [{"_id": 1, "tags": [1, 2], "category": [1, 2]}]},
)
assertSuccessPartial(
result,
{"writeErrors": [{"code": CANNOT_INDEX_PARALLEL_ARRAYS_ERROR}]},
msg="Parallel arrays should fail",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Tests for compound index properties.

Validates numeric equivalence in compound indexes.
"""

import pytest
from bson import Int64

from documentdb_tests.framework.assertions import assertSuccess
from documentdb_tests.framework.executor import execute_command

pytestmark = pytest.mark.index


def test_compound_numeric_int_query_matches_long(collection):
"""Test query with int matches document with long via compound index."""
execute_command(
collection,
{"createIndexes": collection.name, "indexes": [{"key": {"a": 1, "b": 1}, "name": "ab"}]},
)
collection.insert_one({"_id": 1, "a": Int64(1), "b": Int64(2)})
result = execute_command(collection, {"find": collection.name, "filter": {"a": 1, "b": 2}})
assertSuccess(
result, [{"_id": 1, "a": Int64(1), "b": Int64(2)}], msg="int query matches long in compound"
)
Loading
Loading