PYTHON-5374 Assert unset BulkWriteException.partialResult in CRUD prose tests (#2425)

Co-authored-by: Noah Stapp <noah@noahstapp.com>
This commit is contained in:
Steven Silvester 2025-07-14 10:47:38 -05:00 committed by GitHub
parent e07a6b7e77
commit 5ce53dc175
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 30 deletions

View File

@ -84,6 +84,7 @@ class TestClientBulkWrite(AsyncIntegrationTest):
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Note: tests 1 and 2 are in test_read_write_concern_spec.py
class TestClientBulkWriteCRUD(AsyncIntegrationTest):
async def asyncSetUp(self):
await super().asyncSetUp()
@ -92,7 +93,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.max_message_size_bytes = await async_client_context.max_message_size_bytes
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_batch_splits_if_num_operations_too_large(self):
async def test_3_batch_splits_if_num_operations_too_large(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -116,7 +117,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(first_event.operation_id, second_event.operation_id)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_batch_splits_if_ops_payload_too_large(self):
async def test_4_batch_splits_if_ops_payload_too_large(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -148,7 +149,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
@async_client_context.require_version_min(8, 0, 0, -24)
@async_client_context.require_failCommand_fail_point
async def test_collects_write_concern_errors_across_batches(self):
async def test_5_collects_write_concern_errors_across_batches(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(
event_listeners=[listener],
@ -189,7 +190,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(len(bulk_write_events), 2)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_collects_write_errors_across_batches_unordered(self):
async def test_6_collects_write_errors_across_batches_unordered(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -218,7 +219,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(len(bulk_write_events), 2)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_collects_write_errors_across_batches_ordered(self):
async def test_6_collects_write_errors_across_batches_ordered(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -247,7 +248,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(len(bulk_write_events), 1)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_handles_cursor_requiring_getMore(self):
async def test_7_handles_cursor_requiring_getMore(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -287,7 +288,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
@async_client_context.require_version_min(8, 0, 0, -24)
@async_client_context.require_no_standalone
async def test_handles_cursor_requiring_getMore_within_transaction(self):
async def test_8_handles_cursor_requiring_getMore_within_transaction(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -329,7 +330,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
@async_client_context.require_version_min(8, 0, 0, -24)
@async_client_context.require_failCommand_fail_point
async def test_handles_getMore_error(self):
async def test_9_handles_getMore_error(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -382,7 +383,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertTrue(kill_cursors_event)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_returns_error_if_unacknowledged_too_large_insert(self):
async def test_10_returns_error_if_unacknowledged_too_large_insert(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -441,7 +442,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
return num_models, models
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_no_batch_splits_if_new_namespace_is_not_too_large(self):
async def test_11_no_batch_splits_if_new_namespace_is_not_too_large(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -471,7 +472,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(event.command["nsInfo"][0]["ns"], "db.coll")
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_batch_splits_if_new_namespace_is_too_large(self):
async def test_11_batch_splits_if_new_namespace_is_too_large(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -508,25 +509,27 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(second_event.command["nsInfo"][0]["ns"], namespace)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_returns_error_if_no_writes_can_be_added_to_ops(self):
async def test_12_returns_error_if_no_writes_can_be_added_to_ops(self):
client = await self.async_rs_or_single_client()
# Document too large.
b_repeated = "b" * self.max_message_size_bytes
models = [InsertOne(namespace="db.coll", document={"a": b_repeated})]
with self.assertRaises(DocumentTooLarge):
with self.assertRaises(DocumentTooLarge) as context:
await client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
# Namespace too large.
c_repeated = "c" * self.max_message_size_bytes
namespace = f"db.{c_repeated}"
models = [InsertOne(namespace=namespace, document={"a": "b"})]
with self.assertRaises(DocumentTooLarge):
with self.assertRaises(DocumentTooLarge) as context:
await client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
@async_client_context.require_version_min(8, 0, 0, -24)
@unittest.skipUnless(_HAVE_PYMONGOCRYPT, "pymongocrypt is not installed")
async def test_returns_error_if_auto_encryption_configured(self):
async def test_13_returns_error_if_auto_encryption_configured(self):
opts = AutoEncryptionOpts(
key_vault_namespace="db.coll",
kms_providers={"aws": {"accessKeyId": "foo", "secretAccessKey": "bar"}},
@ -536,6 +539,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
models = [InsertOne(namespace="db.coll", document={"a": "b"})]
with self.assertRaises(InvalidOperation) as context:
await client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
self.assertIn(
"bulk_write does not currently support automatic encryption", context.exception._message
)
@ -579,6 +583,8 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(result.update_results[1].did_upsert, True)
self.assertEqual(result.update_results[2].did_upsert, False)
# Note: test 14 is optional and intentionally not implemented because we provide multiple APIs to specify explain.
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_15_unacknowledged_write_across_batches(self):
listener = OvertCommandListener()

View File

@ -180,6 +180,8 @@ class TestReadWriteConcernSpec(AsyncIntegrationTest):
WriteConcern(w=async_client_context.w, wtimeout=1), WTimeoutError
)
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Test 1 (included here instead of test_client_bulk_write.py)
@async_client_context.require_failCommand_fail_point
async def test_error_includes_errInfo(self):
expected_wce = {
@ -214,6 +216,8 @@ class TestReadWriteConcernSpec(AsyncIntegrationTest):
}
self.assertEqual(ctx.exception.details, expected_details)
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Test 2 (included here instead of test_client_bulk_write.py)
@async_client_context.require_version_min(4, 9)
async def test_write_error_details_exposes_errinfo(self):
listener = OvertCommandListener()

View File

@ -84,6 +84,7 @@ class TestClientBulkWrite(IntegrationTest):
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Note: tests 1 and 2 are in test_read_write_concern_spec.py
class TestClientBulkWriteCRUD(IntegrationTest):
def setUp(self):
super().setUp()
@ -92,7 +93,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.max_message_size_bytes = client_context.max_message_size_bytes
@client_context.require_version_min(8, 0, 0, -24)
def test_batch_splits_if_num_operations_too_large(self):
def test_3_batch_splits_if_num_operations_too_large(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -116,7 +117,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(first_event.operation_id, second_event.operation_id)
@client_context.require_version_min(8, 0, 0, -24)
def test_batch_splits_if_ops_payload_too_large(self):
def test_4_batch_splits_if_ops_payload_too_large(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -148,7 +149,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
@client_context.require_version_min(8, 0, 0, -24)
@client_context.require_failCommand_fail_point
def test_collects_write_concern_errors_across_batches(self):
def test_5_collects_write_concern_errors_across_batches(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(
event_listeners=[listener],
@ -189,7 +190,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(len(bulk_write_events), 2)
@client_context.require_version_min(8, 0, 0, -24)
def test_collects_write_errors_across_batches_unordered(self):
def test_6_collects_write_errors_across_batches_unordered(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -218,7 +219,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(len(bulk_write_events), 2)
@client_context.require_version_min(8, 0, 0, -24)
def test_collects_write_errors_across_batches_ordered(self):
def test_6_collects_write_errors_across_batches_ordered(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -247,7 +248,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(len(bulk_write_events), 1)
@client_context.require_version_min(8, 0, 0, -24)
def test_handles_cursor_requiring_getMore(self):
def test_7_handles_cursor_requiring_getMore(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -287,7 +288,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
@client_context.require_version_min(8, 0, 0, -24)
@client_context.require_no_standalone
def test_handles_cursor_requiring_getMore_within_transaction(self):
def test_8_handles_cursor_requiring_getMore_within_transaction(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -329,7 +330,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
@client_context.require_version_min(8, 0, 0, -24)
@client_context.require_failCommand_fail_point
def test_handles_getMore_error(self):
def test_9_handles_getMore_error(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -382,7 +383,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertTrue(kill_cursors_event)
@client_context.require_version_min(8, 0, 0, -24)
def test_returns_error_if_unacknowledged_too_large_insert(self):
def test_10_returns_error_if_unacknowledged_too_large_insert(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -437,7 +438,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
return num_models, models
@client_context.require_version_min(8, 0, 0, -24)
def test_no_batch_splits_if_new_namespace_is_not_too_large(self):
def test_11_no_batch_splits_if_new_namespace_is_not_too_large(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -467,7 +468,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(event.command["nsInfo"][0]["ns"], "db.coll")
@client_context.require_version_min(8, 0, 0, -24)
def test_batch_splits_if_new_namespace_is_too_large(self):
def test_11_batch_splits_if_new_namespace_is_too_large(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -504,25 +505,27 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(second_event.command["nsInfo"][0]["ns"], namespace)
@client_context.require_version_min(8, 0, 0, -24)
def test_returns_error_if_no_writes_can_be_added_to_ops(self):
def test_12_returns_error_if_no_writes_can_be_added_to_ops(self):
client = self.rs_or_single_client()
# Document too large.
b_repeated = "b" * self.max_message_size_bytes
models = [InsertOne(namespace="db.coll", document={"a": b_repeated})]
with self.assertRaises(DocumentTooLarge):
with self.assertRaises(DocumentTooLarge) as context:
client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
# Namespace too large.
c_repeated = "c" * self.max_message_size_bytes
namespace = f"db.{c_repeated}"
models = [InsertOne(namespace=namespace, document={"a": "b"})]
with self.assertRaises(DocumentTooLarge):
with self.assertRaises(DocumentTooLarge) as context:
client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
@client_context.require_version_min(8, 0, 0, -24)
@unittest.skipUnless(_HAVE_PYMONGOCRYPT, "pymongocrypt is not installed")
def test_returns_error_if_auto_encryption_configured(self):
def test_13_returns_error_if_auto_encryption_configured(self):
opts = AutoEncryptionOpts(
key_vault_namespace="db.coll",
kms_providers={"aws": {"accessKeyId": "foo", "secretAccessKey": "bar"}},
@ -532,6 +535,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
models = [InsertOne(namespace="db.coll", document={"a": "b"})]
with self.assertRaises(InvalidOperation) as context:
client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
self.assertIn(
"bulk_write does not currently support automatic encryption", context.exception._message
)
@ -575,6 +579,8 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(result.update_results[1].did_upsert, True)
self.assertEqual(result.update_results[2].did_upsert, False)
# Note: test 14 is optional and intentionally not implemented because we provide multiple APIs to specify explain.
@client_context.require_version_min(8, 0, 0, -24)
def test_15_unacknowledged_write_across_batches(self):
listener = OvertCommandListener()

View File

@ -178,6 +178,8 @@ class TestReadWriteConcernSpec(IntegrationTest):
self.disable_replication(client_context.client)
self.assertWriteOpsRaise(WriteConcern(w=client_context.w, wtimeout=1), WTimeoutError)
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Test 1 (included here instead of test_client_bulk_write.py)
@client_context.require_failCommand_fail_point
def test_error_includes_errInfo(self):
expected_wce = {
@ -212,6 +214,8 @@ class TestReadWriteConcernSpec(IntegrationTest):
}
self.assertEqual(ctx.exception.details, expected_details)
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Test 2 (included here instead of test_client_bulk_write.py)
@client_context.require_version_min(4, 9)
def test_write_error_details_exposes_errinfo(self):
listener = OvertCommandListener()