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
@@ -0,0 +1,116 @@
"""Tests for abortTransaction 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 NoSuchTransaction).
"""

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={"abortTransaction": 1, "autocommit": False},
error_code=INVALID_OPTIONS_ERROR,
msg="abortTransaction should reject autocommit:false outside txn with InvalidOptions",
),
CommandTestCase(
"autocommit_bool_true",
command={"abortTransaction": 1, "autocommit": True},
error_code=INVALID_OPTIONS_ERROR,
msg="abortTransaction 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={"abortTransaction": 1, "autocommit": 0},
error_code=TYPE_MISMATCH_ERROR,
msg="abortTransaction should reject autocommit:0 (int32) as wrong type",
),
CommandTestCase(
"autocommit_int32_one",
command={"abortTransaction": 1, "autocommit": 1},
error_code=TYPE_MISMATCH_ERROR,
msg="abortTransaction should reject autocommit:1 (int32) as wrong type",
),
CommandTestCase(
"autocommit_int64_zero",
command={"abortTransaction": 1, "autocommit": Int64(0)},
error_code=TYPE_MISMATCH_ERROR,
msg="abortTransaction should reject autocommit:Int64(0) as wrong type",
),
CommandTestCase(
"autocommit_double_zero",
command={"abortTransaction": 1, "autocommit": 0.0},
error_code=TYPE_MISMATCH_ERROR,
msg="abortTransaction should reject autocommit:0.0 as wrong type",
),
CommandTestCase(
"autocommit_decimal128_zero",
command={"abortTransaction": 1, "autocommit": Decimal128("0")},
error_code=TYPE_MISMATCH_ERROR,
msg="abortTransaction should reject autocommit:Decimal128('0') as wrong type",
),
CommandTestCase(
"autocommit_string",
command={"abortTransaction": 1, "autocommit": "false"},
error_code=TYPE_MISMATCH_ERROR,
msg="abortTransaction should reject autocommit:'false' (string) as wrong type",
),
CommandTestCase(
"autocommit_object",
command={"abortTransaction": 1, "autocommit": {}},
error_code=TYPE_MISMATCH_ERROR,
msg="abortTransaction should reject autocommit:{} (object) as wrong type",
),
CommandTestCase(
"autocommit_array",
command={"abortTransaction": 1, "autocommit": []},
error_code=TYPE_MISMATCH_ERROR,
msg="abortTransaction 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={"abortTransaction": 1, "autocommit": None},
error_code=COMMAND_FAILED_ERROR,
msg="abortTransaction 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_abortTransaction_autocommit_error(collection, test):
"""Test abortTransaction autocommit error cases."""
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,185 @@
"""Tests for abortTransaction comment parameter type acceptance in a real transaction.

Validates that the comment parameter accepts any BSON type when
abortTransaction 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.sessions.commands.utils.session_test_case import (
SessionOp,
SessionOperation,
SessionTestCase,
execute_session_command,
)
from documentdb_tests.framework.assertions import assertSuccessPartial
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[SessionTestCase] = [
SessionTestCase(
"comment_string",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": "test comment"},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:string",
),
SessionTestCase(
"comment_string_empty",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": ""},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:empty string",
),
SessionTestCase(
"comment_int32",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": 42},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:int32",
),
SessionTestCase(
"comment_int64",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": Int64(42)},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:Int64",
),
SessionTestCase(
"comment_double",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": 3.14},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:double",
),
SessionTestCase(
"comment_decimal128",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": Decimal128("1.5")},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:Decimal128",
),
SessionTestCase(
"comment_bool_true",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": True},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:true",
),
SessionTestCase(
"comment_bool_false",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": False},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:false",
),
SessionTestCase(
"comment_null",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": None},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:null",
),
SessionTestCase(
"comment_object",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": {"key": "value"}},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:object",
),
SessionTestCase(
"comment_object_empty",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": {}},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:empty object",
),
SessionTestCase(
"comment_array",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": [1, 2, 3]},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:array",
),
SessionTestCase(
"comment_array_empty",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": []},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:empty array",
),
SessionTestCase(
"comment_objectid",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": ObjectId()},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:ObjectId",
),
SessionTestCase(
"comment_datetime",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={
"abortTransaction": 1,
"comment": datetime(2024, 1, 1, tzinfo=timezone.utc),
},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:datetime",
),
SessionTestCase(
"comment_binary",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": Binary(b"\x00")},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:Binary",
),
SessionTestCase(
"comment_regex",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": Regex(".*")},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:Regex",
),
SessionTestCase(
"comment_timestamp",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": Timestamp(0, 0)},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:Timestamp",
),
SessionTestCase(
"comment_minkey",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": MinKey()},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:MinKey",
),
SessionTestCase(
"comment_maxkey",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": MaxKey()},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:MaxKey",
),
SessionTestCase(
"comment_code",
ops=[SessionOperation(op=SessionOp.INSERT, document={"_id": 1})],
commit_command={"abortTransaction": 1, "comment": Code("function(){}")},
expected_response={"ok": 1.0},
msg="abortTransaction should accept comment:Code",
),
]


@pytest.mark.admin

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the use of it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pytest.mark.admin allows users to filter with pytest -m admin to run only admin command tests. This tag indicates the test is an admin command test

@pytest.mark.parametrize("test", pytest_params(COMMENT_TYPE_TESTS))
def test_abortTransaction_comment(collection, test):
"""Test abortTransaction comment parameter type acceptance in a transaction."""
result = execute_session_command(collection, test, abort=True)
assertSuccessPartial(result, test.expected_response, msg=test.msg)
Loading
Loading