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,67 @@
"""Tests for commitTransaction command success behavior.

Each test drives the full transaction lifecycle inline (start session, start
transaction, run an operation, commit) so that setup -> act -> assert reads
top to bottom. These cover the fundamental commit behaviors: a committed write
is durable, an empty transaction commits, the response carries ok:1, and a
commit is retryable.
"""

from __future__ import annotations

import pytest

from documentdb_tests.framework.assertions import (
assertNotError,
assertSuccess,
assertSuccessPartial,
)
from documentdb_tests.framework.executor import execute_admin_command, execute_command

pytestmark = [pytest.mark.admin, pytest.mark.requires(transactions=True)]


def test_commitTransaction_persists_insert(collection):
"""A committed insert is visible outside the transaction after commit."""
client = collection.database.client
with client.start_session() as session:
session.start_transaction()
collection.insert_one({"_id": 1, "x": "inserted"}, session=session)
session.commit_transaction()
readback = execute_command(collection, {"find": collection.name, "filter": {}})
assertSuccess(readback, [{"_id": 1, "x": "inserted"}])


def test_commitTransaction_empty(collection):
"""Committing a transaction with no operations succeeds."""
client = collection.database.client
with client.start_session() as session:
session.start_transaction()
session.commit_transaction()
readback = execute_command(collection, {"find": collection.name, "filter": {}})
assertNotError(readback, msg="commitTransaction on an empty transaction should not error")


def test_commitTransaction_response_ok(collection):
"""The commitTransaction command response reports ok:1 on success."""
client = collection.database.client
with client.start_session() as session:
session.start_transaction()
collection.insert_one({"_id": 1}, session=session)
result = execute_admin_command(collection, {"commitTransaction": 1}, session=session)
assertSuccessPartial(result, {"ok": 1.0})


def test_commitTransaction_is_retryable(collection):
"""Re-sending commitTransaction after a successful commit returns ok:1.

The committed state is retained for retryability, so the retry is a no-op
that succeeds rather than an error.
"""
client = collection.database.client
with client.start_session() as session:
session.start_transaction()
collection.insert_one({"_id": 1}, session=session)
execute_admin_command(collection, {"commitTransaction": 1}, session=session)
retry = execute_admin_command(collection, {"commitTransaction": 1}, session=session)
assertSuccessPartial(retry, {"ok": 1.0})
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Tests for cross-collection atomicity of a committed transaction.

Writes to more than one collection in a single transaction are committed
atomically: after commit, the writes to the second collection are durable,
demonstrating the commit spans every collection touched in the transaction.
"""

from __future__ import annotations

import pytest

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

pytestmark = [pytest.mark.admin, pytest.mark.requires(transactions=True)]


def test_commit_persists_writes_across_collections(collection):
"""Committing persists writes made to a second collection in the same txn."""
client = collection.database.client
other = collection.database[f"{collection.name}_other"]
with client.start_session() as session:
session.start_transaction()
collection.insert_one({"_id": 1}, session=session)
other.insert_one({"_id": 2}, session=session)
session.commit_transaction()
readback = execute_command(collection, {"find": other.name, "filter": {}})
assertSuccess(readback, [{"_id": 2}])
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""Tests for commitTransaction autocommit parameter validation.

Validates type and value acceptance for the autocommit parameter. Per the
MongoDB documentation, autocommit must be literal boolean false. Boolean true
produces InvalidOptions, non-boolean types produce TypeMismatch, and null is
treated as omitted (falls through to the no-transaction error).
"""

from __future__ import annotations

import pytest
from bson import Decimal128, Int64

from documentdb_tests.compatibility.tests.core.utils.command_test_case import (
CommandTestCase,
)
from documentdb_tests.framework.assertions import assertFailureCode
from documentdb_tests.framework.error_codes import (
COMMAND_FAILED_ERROR,
INVALID_OPTIONS_ERROR,
TYPE_MISMATCH_ERROR,
)
from documentdb_tests.framework.executor import execute_admin_command
from documentdb_tests.framework.parametrize import pytest_params

pytestmark = [pytest.mark.admin, pytest.mark.requires(transactions=True)]


# Property [autocommit Boolean Values]: autocommit accepts only boolean values.
AUTOCOMMIT_BOOLEAN_TESTS: list[CommandTestCase] = [
CommandTestCase(
"autocommit_bool_false",
command={"commitTransaction": 1, "autocommit": False},
error_code=INVALID_OPTIONS_ERROR,
msg="commitTransaction should reject autocommit:false outside txn with InvalidOptions",
),
CommandTestCase(
"autocommit_bool_true",
command={"commitTransaction": 1, "autocommit": True},
error_code=INVALID_OPTIONS_ERROR,
msg="commitTransaction should reject autocommit:true with InvalidOptions",
),
]

# Property [autocommit Type Strictness]: non-boolean types are rejected with TypeMismatch.
AUTOCOMMIT_TYPE_REJECTION_TESTS: list[CommandTestCase] = [
CommandTestCase(
"autocommit_int32_zero",
command={"commitTransaction": 1, "autocommit": 0},
error_code=TYPE_MISMATCH_ERROR,
msg="commitTransaction should reject autocommit:0 (int32) as wrong type",
),
CommandTestCase(
"autocommit_int32_one",
command={"commitTransaction": 1, "autocommit": 1},
error_code=TYPE_MISMATCH_ERROR,
msg="commitTransaction should reject autocommit:1 (int32) as wrong type",
),
CommandTestCase(
"autocommit_int64_zero",
command={"commitTransaction": 1, "autocommit": Int64(0)},
error_code=TYPE_MISMATCH_ERROR,
msg="commitTransaction should reject autocommit:Int64(0) as wrong type",
),
CommandTestCase(
"autocommit_double_zero",
command={"commitTransaction": 1, "autocommit": 0.0},
error_code=TYPE_MISMATCH_ERROR,
msg="commitTransaction should reject autocommit:0.0 as wrong type",
),
CommandTestCase(
"autocommit_decimal128_zero",
command={"commitTransaction": 1, "autocommit": Decimal128("0")},
error_code=TYPE_MISMATCH_ERROR,
msg="commitTransaction should reject autocommit:Decimal128('0') as wrong type",
),
CommandTestCase(
"autocommit_string",
command={"commitTransaction": 1, "autocommit": "false"},
error_code=TYPE_MISMATCH_ERROR,
msg="commitTransaction should reject autocommit:'false' (string) as wrong type",
),
CommandTestCase(
"autocommit_object",
command={"commitTransaction": 1, "autocommit": {}},
error_code=TYPE_MISMATCH_ERROR,
msg="commitTransaction should reject autocommit:{} (object) as wrong type",
),
CommandTestCase(
"autocommit_array",
command={"commitTransaction": 1, "autocommit": []},
error_code=TYPE_MISMATCH_ERROR,
msg="commitTransaction should reject autocommit:[] (array) as wrong type",
),
]

# Property [autocommit Null Handling]: null autocommit is treated as omitted.
AUTOCOMMIT_NULL_TESTS: list[CommandTestCase] = [
CommandTestCase(
"autocommit_null",
command={"commitTransaction": 1, "autocommit": None},
error_code=COMMAND_FAILED_ERROR,
msg="commitTransaction should treat autocommit:null as omitted",
),
]

AUTOCOMMIT_TESTS: list[CommandTestCase] = (
AUTOCOMMIT_BOOLEAN_TESTS + AUTOCOMMIT_TYPE_REJECTION_TESTS + AUTOCOMMIT_NULL_TESTS
)


@pytest.mark.parametrize("test", pytest_params(AUTOCOMMIT_TESTS))
def test_commitTransaction_autocommit_error(collection, test):
"""Test commitTransaction autocommit parameter validation."""
result = execute_admin_command(collection, test.command)
assertFailureCode(result, test.error_code, msg=test.msg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
"""Tests for commitTransaction comment parameter type acceptance in a real transaction.

Validates that the comment parameter accepts any BSON type when
commitTransaction is issued inside an active transaction on a replica set.
"""

from __future__ import annotations

from datetime import datetime, timezone

import pytest
from bson import Binary, Code, Decimal128, Int64, MaxKey, MinKey, ObjectId, Regex, Timestamp

from documentdb_tests.compatibility.tests.core.utils.command_test_case import (
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

pytestmark = [pytest.mark.admin, pytest.mark.requires(transactions=True)]

# Property [comment Type Acceptance]: comment accepts any BSON type.
COMMENT_TYPE_TESTS: list[CommandTestCase] = [
CommandTestCase(
"comment_string",
command={"commitTransaction": 1, "comment": "test comment"},
msg="commitTransaction should accept comment:string",
),
CommandTestCase(
"comment_string_empty",
command={"commitTransaction": 1, "comment": ""},
msg="commitTransaction should accept comment:empty string",
),
CommandTestCase(
"comment_int32",
command={"commitTransaction": 1, "comment": 42},
msg="commitTransaction should accept comment:int32",
),
CommandTestCase(
"comment_int64",
command={"commitTransaction": 1, "comment": Int64(42)},
msg="commitTransaction should accept comment:Int64",
),
CommandTestCase(
"comment_double",
command={"commitTransaction": 1, "comment": 3.14},
msg="commitTransaction should accept comment:double",
),
CommandTestCase(
"comment_decimal128",
command={"commitTransaction": 1, "comment": Decimal128("1.5")},
msg="commitTransaction should accept comment:Decimal128",
),
CommandTestCase(
"comment_bool_true",
command={"commitTransaction": 1, "comment": True},
msg="commitTransaction should accept comment:true",
),
CommandTestCase(
"comment_bool_false",
command={"commitTransaction": 1, "comment": False},
msg="commitTransaction should accept comment:false",
),
CommandTestCase(
"comment_null",
command={"commitTransaction": 1, "comment": None},
msg="commitTransaction should accept comment:null",
),
CommandTestCase(
"comment_object",
command={"commitTransaction": 1, "comment": {"key": "value"}},
msg="commitTransaction should accept comment:object",
),
CommandTestCase(
"comment_object_empty",
command={"commitTransaction": 1, "comment": {}},
msg="commitTransaction should accept comment:empty object",
),
CommandTestCase(
"comment_array",
command={"commitTransaction": 1, "comment": [1, 2, 3]},
msg="commitTransaction should accept comment:array",
),
CommandTestCase(
"comment_array_empty",
command={"commitTransaction": 1, "comment": []},
msg="commitTransaction should accept comment:empty array",
),
CommandTestCase(
"comment_objectid",
command={"commitTransaction": 1, "comment": ObjectId()},
msg="commitTransaction should accept comment:ObjectId",
),
CommandTestCase(
"comment_datetime",
command={
"commitTransaction": 1,
"comment": datetime(2024, 1, 1, tzinfo=timezone.utc),
},
msg="commitTransaction should accept comment:datetime",
),
CommandTestCase(
"comment_binary",
command={"commitTransaction": 1, "comment": Binary(b"\x00")},
msg="commitTransaction should accept comment:Binary",
),
CommandTestCase(
"comment_regex",
command={"commitTransaction": 1, "comment": Regex(".*")},
msg="commitTransaction should accept comment:Regex",
),
CommandTestCase(
"comment_timestamp",
command={"commitTransaction": 1, "comment": Timestamp(0, 0)},
msg="commitTransaction should accept comment:Timestamp",
),
CommandTestCase(
"comment_minkey",
command={"commitTransaction": 1, "comment": MinKey()},
msg="commitTransaction should accept comment:MinKey",
),
CommandTestCase(
"comment_maxkey",
command={"commitTransaction": 1, "comment": MaxKey()},
msg="commitTransaction should accept comment:MaxKey",
),
CommandTestCase(
"comment_code",
command={"commitTransaction": 1, "comment": Code("function(){}")},
msg="commitTransaction should accept comment:Code",
),
]


@pytest.mark.parametrize("test", pytest_params(COMMENT_TYPE_TESTS))
def test_commitTransaction_comment(collection, test):
"""Test commitTransaction comment parameter type acceptance in a transaction."""
client = collection.database.client
with client.start_session() as session:
session.start_transaction()
collection.insert_one({"_id": 1}, session=session)
result = execute_admin_command(collection, test.command, session=session)
assertSuccessPartial(result, {"ok": 1.0}, msg=test.msg)
Loading
Loading