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
1 change: 1 addition & 0 deletions .evergreen/resync-specs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ do
;;
read-write-concern|read_write_concern)
cpjson read-write-concern/tests/operation read_write_concern/operation
cpjson read-write-concern/tests/write-concern-errors read_write_concern/write-concern-errors
;;
retryable-reads|retryable_reads)
cpjson retryable-reads/tests/ retryable_reads
Expand Down
17 changes: 12 additions & 5 deletions test/asynchronous/test_read_write_concern_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

_IS_SYNC = False

TEST_PATH = get_test_path("read_write_concern")
_TEST_PATH = get_test_path("read_write_concern")


class TestReadWriteConcernSpec(AsyncIntegrationTest):
Expand Down Expand Up @@ -298,11 +298,11 @@ def run_test(self):


def create_tests():
for dirpath, _, filenames in os.walk(TEST_PATH):
for dirpath, _, filenames in os.walk(_TEST_PATH):
dirname = os.path.split(dirpath)[-1]

if dirname == "operation":
# This directory is tested by TestOperations.
if dirname == "operation" or dirname == "write-concern-errors":
# These directories have their tests generated below by generate_test_classes().
continue
elif dirname == "connection-string":
create_test = create_connection_string_test
Expand Down Expand Up @@ -333,11 +333,18 @@ def create_tests():
# PyMongo does not support MapReduce.
globals().update(
generate_test_classes(
os.path.join(TEST_PATH, "operation"),
os.path.join(_TEST_PATH, "operation"),
module=__name__,
expected_failures=["MapReduce .*"],
)
)
globals().update(
generate_test_classes(
os.path.join(_TEST_PATH, "write-concern-errors"),
module=__name__,
expected_failures=["(?i)MapReduce .*"],
)
)


if __name__ == "__main__":
Expand Down
49 changes: 31 additions & 18 deletions test/asynchronous/unified_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,16 @@ async def is_run_on_requirement_satisfied(requirement):
)


def _unwrap_server_error(exception):
"""Return the error that carries the server's response details and labels."""
if isinstance(exception, ClientBulkWriteException) and exception.error is not None:
return exception.error
# EncryptionError wraps the original exception.
if isinstance(exception, EncryptionError) and isinstance(exception.cause, PyMongoError):
return exception.cause
return exception


class NonLazyCursor:
"""A find cursor proxy that creates the remote cursor when initialized."""

Expand Down Expand Up @@ -673,11 +683,10 @@ def process_error(self, exception, spec):
error_labels_omit = spec.get("errorLabelsOmit")
expect_result = spec.get("expectResult")
error_response = spec.get("errorResponse")
write_concern_errors = spec.get("writeConcernErrors")
server_error = _unwrap_server_error(exception)
if error_response:
if isinstance(exception, ClientBulkWriteException):
self.match_evaluator.match_result(error_response, exception.error.details)
else:
self.match_evaluator.match_result(error_response, exception.details)
self.match_evaluator.match_result(error_response, server_error.details)

if is_error:
# already satisfied because exception was raised
Expand Down Expand Up @@ -714,32 +723,36 @@ def process_error(self, exception, spec):
self.assertIn(error_contains.lower(), errmsg)

if error_code:
if isinstance(exception, ClientBulkWriteException):
self.assertEqual(error_code, exception.error.details.get("code"))
else:
self.assertEqual(error_code, exception.details.get("code"))
self.assertEqual(error_code, server_error.details.get("code"))

if error_code_name:
if isinstance(exception, ClientBulkWriteException):
self.assertEqual(error_code, exception.error.details.get("codeName"))
else:
self.assertEqual(error_code_name, exception.details.get("codeName"))
self.assertEqual(error_code_name, server_error.details.get("codeName"))

if error_labels_contain:
if isinstance(exception, ClientBulkWriteException):
error = exception.error
else:
error = exception
labels = [
err_label for err_label in error_labels_contain if error.has_error_label(err_label)
err_label
for err_label in error_labels_contain
if server_error.has_error_label(err_label)
]
self.assertEqual(labels, error_labels_contain)

if error_labels_omit:
for err_label in error_labels_omit:
if exception.has_error_label(err_label):
if server_error.has_error_label(err_label):
self.fail(f"Exception '{exception}' unexpectedly had label '{err_label}'")

if write_concern_errors is not None:
if not isinstance(exception, ClientBulkWriteException):
self.fail(
f"writeConcernErrors can only be specified with {ClientBulkWriteException} exceptions, got {exception}"
)
actual_write_concern_errors = exception.details.get("writeConcernErrors", [])
self.assertEqual(len(write_concern_errors), len(actual_write_concern_errors))
for expected_wce, actual_wce in zip(write_concern_errors, actual_write_concern_errors):
# Use the spec's WriteConcernError field names.
formatted_actual_wce = {"code": actual_wce["code"], "message": actual_wce["errmsg"]}
self.match_evaluator.match_result(expected_wce, formatted_actual_wce)

if expect_result:
if isinstance(exception, BulkWriteError):
result = parse_bulk_write_error_result(exception)
Expand Down
133 changes: 133 additions & 0 deletions test/read_write_concern/write-concern-errors/aggregate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
{
"description": "write-concern-errors-aggregate",
"schemaVersion": "1.0",
"createEntities": [
{
"client": {
"id": "client0",
"useMultipleMongoses": false
}
},
{
"database": {
"id": "database0",
"client": "client0",
"databaseName": "write-concern-errors-tests"
}
},
{
"collection": {
"id": "collection0",
"database": "database0",
"collectionName": "coll"
}
}
],
"initialData": [
{
"collectionName": "coll",
"databaseName": "write-concern-errors-tests",
"documents": []
}
],
"tests": [
{
"description": "aggregate with $out reports a writeConcernError",
"operations": [
{
"name": "failPoint",
"object": "testRunner",
"arguments": {
"client": "client0",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"aggregate"
],
"writeConcernError": {
"code": 79,
"codeName": "UnknownReplWriteConcern"
}
}
}
}
},
{
"name": "aggregate",
"object": "collection0",
"arguments": {
"pipeline": [
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "aggregate_out"
}
]
},
"expectError": {
"errorCode": 79
}
}
]
},
{
"description": "aggregate with $merge reports a writeConcernError",
"operations": [
{
"name": "failPoint",
"object": "testRunner",
"arguments": {
"client": "client0",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"aggregate"
],
"writeConcernError": {
"code": 79,
"codeName": "UnknownReplWriteConcern"
}
}
}
}
},
{
"name": "aggregate",
"object": "collection0",
"arguments": {
"pipeline": [
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$merge": {
"into": "aggregate_merge"
}
}
]
},
"expectError": {
"errorCode": 79
}
}
]
}
]
}
73 changes: 73 additions & 0 deletions test/read_write_concern/write-concern-errors/bulkWrite.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"description": "write-concern-errors-bulkWrite",
"schemaVersion": "1.0",
"createEntities": [
{
"client": {
"id": "client0",
"useMultipleMongoses": false
}
},
{
"database": {
"id": "database0",
"client": "client0",
"databaseName": "write-concern-errors-tests"
}
},
{
"collection": {
"id": "collection0",
"database": "database0",
"collectionName": "coll"
}
}
],
"tests": [
{
"description": "bulkWrite reports a writeConcernError",
"operations": [
{
"name": "failPoint",
"object": "testRunner",
"arguments": {
"client": "client0",
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"insert"
],
"writeConcernError": {
"code": 79,
"codeName": "UnknownReplWriteConcern"
}
}
}
}
},
{
"name": "bulkWrite",
"object": "collection0",
"arguments": {
"requests": [
{
"insertOne": {
"document": {
"x": 33
}
}
}
]
},
"expectError": {
"isError": true
}
}
]
}
]
}
Loading
Loading