Skip to content
Open
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
@@ -1,4 +1,4 @@
"""Tests for collation in bulkWrite operations."""
"""Tests for collation in the bulkWrite command."""

from __future__ import annotations

Expand All @@ -8,13 +8,14 @@
CommandContext,
CommandTestCase,
)
from documentdb_tests.framework.assertions import assertResult
from documentdb_tests.framework.executor import execute_command
from documentdb_tests.framework.assertions import assertSuccessPartial
from documentdb_tests.framework.executor import execute_admin_command
from documentdb_tests.framework.parametrize import pytest_params
from documentdb_tests.framework.target_collection import CustomCollection

# Property [BulkWrite Update Collation]: individual update operations within a
# bulkWrite can specify collation, affecting filter matching independently.
# bulkWrite command can specify collation, affecting filter matching
# independently of other operations in the same command.
COLLATION_BULK_UPDATE_TESTS: list[CommandTestCase] = [
CommandTestCase(
"bulk_update_case_insensitive",
Expand All @@ -23,21 +24,24 @@
{"_id": 2, "x": "banana", "v": 1},
],
command=lambda ctx: {
"update": ctx.collection,
"updates": [
"bulkWrite": 1,
"ops": [
{
"q": {"x": "apple"},
"u": {"$set": {"v": 2}},
"update": 0,
"filter": {"x": "apple"},
"updateMods": {"$set": {"v": 2}},
"collation": {"locale": "en", "strength": 2},
},
{
"q": {"x": "BANANA"},
"u": {"$set": {"v": 3}},
"update": 0,
"filter": {"x": "BANANA"},
"updateMods": {"$set": {"v": 3}},
"collation": {"locale": "en", "strength": 2},
},
],
"nsInfo": [{"ns": ctx.namespace}],
},
expected={"ok": 1.0, "n": 2, "nModified": 2},
expected={"ok": 1.0, "nMatched": 2, "nModified": 2},
msg="bulkWrite updates should each use their own collation",
),
CommandTestCase(
Expand All @@ -47,26 +51,30 @@
{"_id": 2, "x": "banana", "v": 1},
],
command=lambda ctx: {
"update": ctx.collection,
"updates": [
"bulkWrite": 1,
"ops": [
{
"q": {"x": "apple"},
"u": {"$set": {"v": 2}},
"update": 0,
"filter": {"x": "apple"},
"updateMods": {"$set": {"v": 2}},
"collation": {"locale": "en", "strength": 2},
},
{
"q": {"x": "BANANA"},
"u": {"$set": {"v": 3}},
"update": 0,
"filter": {"x": "BANANA"},
"updateMods": {"$set": {"v": 3}},
},
],
"nsInfo": [{"ns": ctx.namespace}],
},
expected={"ok": 1.0, "n": 1, "nModified": 1},
expected={"ok": 1.0, "nMatched": 1, "nModified": 1},
msg="bulkWrite with mixed collation: only collated op should match case-insensitively",
),
]

# Property [BulkWrite Delete Collation]: individual delete operations within a
# bulkWrite can specify collation, affecting filter matching independently.
# bulkWrite command can specify collation, affecting filter matching
# independently of other operations in the same command.
COLLATION_BULK_DELETE_TESTS: list[CommandTestCase] = [
CommandTestCase(
"bulk_delete_case_insensitive",
Expand All @@ -76,16 +84,18 @@
{"_id": 3, "x": "cherry"},
],
command=lambda ctx: {
"delete": ctx.collection,
"deletes": [
"bulkWrite": 1,
"ops": [
{
"q": {"x": "apple"},
"limit": 0,
"delete": 0,
"filter": {"x": "apple"},
"multi": True,
"collation": {"locale": "en", "strength": 2},
},
],
"nsInfo": [{"ns": ctx.namespace}],
},
expected={"ok": 1.0, "n": 1},
expected={"ok": 1.0, "nDeleted": 1},
msg="bulkWrite delete with collation should match case-insensitively",
),
CommandTestCase(
Expand All @@ -96,15 +106,17 @@
{"_id": 3, "x": "banana"},
],
command=lambda ctx: {
"delete": ctx.collection,
"deletes": [
"bulkWrite": 1,
"ops": [
{
"q": {"x": "apple"},
"limit": 0,
"delete": 0,
"filter": {"x": "apple"},
"multi": True,
},
],
"nsInfo": [{"ns": ctx.namespace}],
},
expected={"ok": 1.0, "n": 1},
expected={"ok": 1.0, "nDeleted": 1},
msg="bulkWrite delete without collation should use binary comparison",
),
]
Expand All @@ -120,15 +132,17 @@
{"_id": 2, "x": "banana", "v": 1},
],
command=lambda ctx: {
"update": ctx.collection,
"updates": [
"bulkWrite": 1,
"ops": [
{
"q": {"x": "apple"},
"u": {"$set": {"v": 2}},
"update": 0,
"filter": {"x": "apple"},
"updateMods": {"$set": {"v": 2}},
},
],
"nsInfo": [{"ns": ctx.namespace}],
},
expected={"ok": 1.0, "n": 1, "nModified": 1},
expected={"ok": 1.0, "nMatched": 1, "nModified": 1},
msg="bulkWrite update should inherit collection default collation",
),
CommandTestCase(
Expand All @@ -139,15 +153,17 @@
{"_id": 2, "x": "banana"},
],
command=lambda ctx: {
"delete": ctx.collection,
"deletes": [
"bulkWrite": 1,
"ops": [
{
"q": {"x": "apple"},
"limit": 0,
"delete": 0,
"filter": {"x": "apple"},
"multi": True,
},
],
"nsInfo": [{"ns": ctx.namespace}],
},
expected={"ok": 1.0, "n": 1},
expected={"ok": 1.0, "nDeleted": 1},
msg="bulkWrite delete should inherit collection default collation",
),
]
Expand All @@ -161,14 +177,8 @@

@pytest.mark.parametrize("test", pytest_params(COLLATION_BULK_WRITE_TESTS))
def test_collation_bulk_write(database_client, collection, test):
"""Test collation behavior in bulkWrite operations."""
"""Test collation behavior in the bulkWrite command."""
collection = test.prepare(database_client, collection)
ctx = CommandContext.from_collection(collection)
result = execute_command(collection, test.build_command(ctx))
assertResult(
result,
expected=test.build_expected(ctx),
error_code=test.error_code,
msg=test.msg,
raw_res=True,
)
result = execute_admin_command(collection, test.build_command(ctx))
assertSuccessPartial(result, test.build_expected(ctx), msg=test.msg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"""Tests for bulkWrite argument acceptance — valid ops, nsInfo, and optional fields."""

import pytest

from documentdb_tests.compatibility.tests.core.utils.command_test_case import (
CommandContext,
CommandTestCase,
)
from documentdb_tests.framework.assertions import assertSuccessPartial
from documentdb_tests.framework.executor import execute_admin_command
from documentdb_tests.framework.parametrize import pytest_params

BULKWRITE_ARGUMENT_TESTS: list[CommandTestCase] = [
CommandTestCase(
"ops_single_operation",
command={"bulkWrite": 1, "ops": [{"insert": 0, "document": {"_id": 1}}]},
expected={"ok": 1.0, "nInserted": 1},
msg="bulkWrite should accept ops with a single operation",
),
CommandTestCase(
"ops_many_operations",
command={
"bulkWrite": 1,
"ops": [{"insert": 0, "document": {"_id": i}} for i in range(50)],
},
expected={"ok": 1.0, "nInserted": 50},
msg="bulkWrite should accept ops with many operations",
),
CommandTestCase(
"let_with_document",
docs=[{"_id": 1, "x": 10}],
command={
"bulkWrite": 1,
"ops": [
{
"update": 0,
"filter": {"$expr": {"$eq": ["$x", "$$targetVal"]}},
"updateMods": {"$set": {"matched": True}},
}
],
"let": {"targetVal": 10},
},
expected={"ok": 1.0, "nMatched": 1, "nModified": 1},
msg="bulkWrite should accept a let document with variables",
),
CommandTestCase(
"ops_only_updates",
docs=[{"_id": 1, "x": 1}, {"_id": 2, "x": 2}],
command={
"bulkWrite": 1,
"ops": [
{"update": 0, "filter": {"_id": 1}, "updateMods": {"$set": {"x": 10}}},
{"update": 0, "filter": {"_id": 2}, "updateMods": {"$set": {"x": 20}}},
],
},
expected={"ok": 1.0, "nMatched": 2, "nModified": 2},
msg="bulkWrite should accept an ops array of only updates",
),
CommandTestCase(
"ops_only_deletes",
docs=[{"_id": 1}, {"_id": 2}],
command={
"bulkWrite": 1,
"ops": [
{"delete": 0, "filter": {"_id": 1}},
{"delete": 0, "filter": {"_id": 2}},
],
},
expected={"ok": 1.0, "nDeleted": 2},
msg="bulkWrite should accept an ops array of only deletes",
),
CommandTestCase(
"writeConcern_valid",
command={
"bulkWrite": 1,
"ops": [{"insert": 0, "document": {"_id": 1}}],
"writeConcern": {"w": 1},
},
expected={"ok": 1.0, "nInserted": 1},
msg="bulkWrite should accept a valid writeConcern",
),
]


@pytest.mark.parametrize("test", pytest_params(BULKWRITE_ARGUMENT_TESTS))
def test_bulkWrite_argument_validation(database_client, collection, test):
"""Test bulkWrite argument acceptance — valid ops, nsInfo, and optional fields."""
collection = test.prepare(database_client, collection)
ctx = CommandContext.from_collection(collection)
command = test.build_command(ctx)
if "nsInfo" not in command:
command = {**command, "nsInfo": [{"ns": ctx.namespace}]}
result = execute_admin_command(collection, command)
assertSuccessPartial(result, test.build_expected(ctx), msg=test.msg)


def test_bulkWrite_accepts_unused_nsInfo_entry(collection):
"""Test bulkWrite accepts an nsInfo array with more namespaces than the ops reference."""
ns = f"{collection.database.name}.{collection.name}"
ns_unused = f"{collection.database.name}.{collection.name}_unused"
result = execute_admin_command(
collection,
{
"bulkWrite": 1,
"ops": [{"insert": 0, "document": {"_id": 1}}],
"nsInfo": [{"ns": ns}, {"ns": ns_unused}],
},
)
assertSuccessPartial(
result,
{"ok": 1.0, "nInserted": 1},
msg="bulkWrite should accept an nsInfo entry that no op references",
)
Loading
Loading