PYTHON-5248 - Drop support for MongoDB 4.0 (#2353)

This commit is contained in:
Noah Stapp 2025-06-20 14:25:19 -04:00 committed by GitHub
parent 4ea0288eaa
commit e2bfa9a590
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 710 additions and 1850 deletions

File diff suppressed because it is too large Load Diff

View File

@ -146,7 +146,7 @@ buildvariants:
COMPRESSOR: zlib
- name: compression-zstd-rhel8
tasks:
- name: .test-standard !.server-4.0
- name: .test-standard !.server-4.2
display_name: Compression zstd RHEL8
run_on:
- rhel87-small
@ -522,13 +522,6 @@ buildvariants:
PYTHON_BINARY: /opt/python/3.9/bin/python3
# Server version tests
- name: mongodb-v4.0
tasks:
- name: .server-version
display_name: "* MongoDB v4.0"
run_on:
- rhel87-small
tags: [coverage_tag]
- name: mongodb-v4.2
tasks:
- name: .server-version
@ -664,11 +657,3 @@ buildvariants:
- rhel87-small
expansions:
STORAGE_ENGINE: inmemory
- name: storage-mmapv1-rhel8
tasks:
- name: .test-standard !.sharded_cluster-auth-ssl .server-4.0
display_name: Storage MMAPv1 RHEL8
run_on:
- rhel87-small
expansions:
STORAGE_ENGINE: mmapv1

View File

@ -25,7 +25,6 @@ from generate_config_utils import (
get_task_name,
get_variant_name,
get_versions_from,
get_versions_until,
handle_c_ext,
write_functions_to_file,
write_tasks_to_file,
@ -196,7 +195,7 @@ def create_compression_variants():
for compressor in "snappy", "zlib", "zstd":
expansions = dict(COMPRESSOR=compressor)
if compressor == "zstd":
tasks = [".test-standard !.server-4.0"]
tasks = [".test-standard !.server-4.2"]
else:
tasks = [".test-standard"]
display_name = get_variant_name(f"Compression {compressor}", host)
@ -249,16 +248,11 @@ def create_pyopenssl_variants():
def create_storage_engine_variants():
host = DEFAULT_HOST
engines = ["InMemory", "MMAPv1"]
engines = ["InMemory"]
variants = []
for engine in engines:
expansions = dict(STORAGE_ENGINE=engine.lower())
if engine == engines[0]:
tasks = [".test-standard .standalone-noauth-nossl"]
else:
# MongoDB 4.2 drops support for MMAPv1
versions = get_versions_until("4.0")
tasks = [f".test-standard !.sharded_cluster-auth-ssl .server-{v}" for v in versions]
tasks = [".test-standard .standalone-noauth-nossl"]
display_name = get_variant_name(f"Storage {engine}", host)
variant = create_variant(tasks, display_name, host=host, expansions=expansions)
variants.append(variant)

View File

@ -21,7 +21,7 @@ from shrub.v3.shrub_service import ShrubService
# Globals
##############
ALL_VERSIONS = ["4.0", "4.2", "4.4", "5.0", "6.0", "7.0", "8.0", "rapid", "latest"]
ALL_VERSIONS = ["4.2", "4.4", "5.0", "6.0", "7.0", "8.0", "rapid", "latest"]
CPYTHONS = ["3.9", "3.10", "3.11", "3.12", "3.13"]
PYPYS = ["pypy3.10"]
ALL_PYTHONS = CPYTHONS + PYPYS

View File

@ -66,8 +66,8 @@ MAX_WIRE_VERSION = 0
MAX_WRITE_BATCH_SIZE = 100000
# What this version of PyMongo supports.
MIN_SUPPORTED_SERVER_VERSION = "4.0"
MIN_SUPPORTED_WIRE_VERSION = 7
MIN_SUPPORTED_SERVER_VERSION = "4.2"
MIN_SUPPORTED_WIRE_VERSION = 8
# MongoDB 8.0
MAX_SUPPORTED_WIRE_VERSION = 25

View File

@ -508,19 +508,6 @@ class ClientContext:
func=func,
)
def require_no_mmap(self, func):
"""Run a test only if the server is not using the MMAPv1 storage
engine. Only works for standalone and replica sets; tests are
run regardless of storage engine on sharded clusters.
"""
def is_not_mmap():
if self.is_mongos:
return True
return self.storage_engine != "mmapv1"
return self._require(is_not_mmap, "Storage engine must not be MMAPv1", func=func)
def require_version_min(self, *ver):
"""Run a test only if the server version is at least ``version``."""
other_version = Version(*ver)
@ -651,7 +638,7 @@ class ClientContext:
def require_change_streams(self, func):
"""Run a test only if the server supports change streams."""
return self.require_no_mmap(self.require_no_standalone(func))
return self.require_no_standalone(func)
def is_topology_type(self, topologies):
unknown = set(topologies) - {
@ -754,8 +741,6 @@ class ClientContext:
return self._require(lambda: self.sessions_enabled, "Sessions not supported", func=func)
def supports_retryable_writes(self):
if self.storage_engine == "mmapv1":
return False
if not self.sessions_enabled:
return False
return self.is_mongos or self.is_rs
@ -769,9 +754,6 @@ class ClientContext:
)
def supports_transactions(self):
if self.storage_engine == "mmapv1":
return False
if self.version.at_least(4, 1, 8):
return self.is_mongos or self.is_rs

View File

@ -508,19 +508,6 @@ class AsyncClientContext:
func=func,
)
def require_no_mmap(self, func):
"""Run a test only if the server is not using the MMAPv1 storage
engine. Only works for standalone and replica sets; tests are
run regardless of storage engine on sharded clusters.
"""
def is_not_mmap():
if self.is_mongos:
return True
return self.storage_engine != "mmapv1"
return self._require(is_not_mmap, "Storage engine must not be MMAPv1", func=func)
def require_version_min(self, *ver):
"""Run a test only if the server version is at least ``version``."""
other_version = Version(*ver)
@ -651,7 +638,7 @@ class AsyncClientContext:
def require_change_streams(self, func):
"""Run a test only if the server supports change streams."""
return self.require_no_mmap(self.require_no_standalone(func))
return self.require_no_standalone(func)
async def is_topology_type(self, topologies):
unknown = set(topologies) - {
@ -754,8 +741,6 @@ class AsyncClientContext:
return self._require(lambda: self.sessions_enabled, "Sessions not supported", func=func)
def supports_retryable_writes(self):
if self.storage_engine == "mmapv1":
return False
if not self.sessions_enabled:
return False
return self.is_mongos or self.is_rs
@ -769,9 +754,6 @@ class AsyncClientContext:
)
def supports_transactions(self):
if self.storage_engine == "mmapv1":
return False
if self.version.at_least(4, 1, 8):
return self.is_mongos or self.is_rs

View File

@ -165,7 +165,7 @@ class AsyncTestBulk(AsyncBulkTestBase):
async def test_update_many(self):
await self._test_update_many({"$set": {"foo": "bar"}})
@async_client_context.require_version_min(4, 1, 11)
@async_client_context.require_version_min(4, 2, 0)
async def test_update_many_pipeline(self):
await self._test_update_many([{"$set": {"foo": "bar"}}])
@ -206,7 +206,7 @@ class AsyncTestBulk(AsyncBulkTestBase):
async def test_update_one(self):
await self._test_update_one({"$set": {"foo": "bar"}})
@async_client_context.require_version_min(4, 1, 11)
@async_client_context.require_version_min(4, 2, 0)
async def test_update_one_pipeline(self):
await self._test_update_one([{"$set": {"foo": "bar"}}])

View File

@ -267,7 +267,7 @@ class APITestsMixin:
# $changeStream.startAtOperationTime was added in 4.0.0.
@no_type_check
@async_client_context.require_version_min(4, 0, 0)
@async_client_context.require_version_min(4, 2, 0)
async def test_start_at_operation_time(self):
optime = await self.get_start_at_operation_time()
@ -436,7 +436,7 @@ class APITestsMixin:
await self._test_get_invalidate_event(change_stream)
@no_type_check
@async_client_context.require_version_min(4, 1, 1)
@async_client_context.require_version_min(4, 2, 0)
async def test_start_after(self):
resume_token = await self.get_resume_token(invalidate=True)
@ -452,7 +452,7 @@ class APITestsMixin:
self.assertEqual(change["fullDocument"], {"_id": 2})
@no_type_check
@async_client_context.require_version_min(4, 1, 1)
@async_client_context.require_version_min(4, 2, 0)
async def test_start_after_resume_process_with_changes(self):
resume_token = await self.get_resume_token(invalidate=True)
@ -563,27 +563,16 @@ class ProseSpecTestsMixin:
)
# Prose test no. 1
@async_client_context.require_version_min(4, 0, 7)
@async_client_context.require_version_min(4, 2, 0)
async def test_update_resume_token(self):
await self._test_update_resume_token(self._get_expected_resume_token)
# Prose test no. 1
@async_client_context.require_version_max(4, 0, 7)
async def test_update_resume_token_legacy(self):
await self._test_update_resume_token(self._get_expected_resume_token_legacy)
# Prose test no. 2
@async_client_context.require_version_min(4, 1, 8)
@async_client_context.require_version_min(4, 2, 0)
async def test_raises_error_on_missing_id_418plus(self):
# Server returns an error on 4.1.8+
await self._test_raises_error_on_missing_id(OperationFailure)
# Prose test no. 2
@async_client_context.require_version_max(4, 1, 8)
async def test_raises_error_on_missing_id_418minus(self):
# PyMongo raises an error
await self._test_raises_error_on_missing_id(InvalidOperation)
# Prose test no. 3
@no_type_check
async def test_resume_on_error(self):
@ -642,40 +631,12 @@ class ProseSpecTestsMixin:
cursor.close = raise_error
await self.insert_one_and_check(change_stream, {"_id": 2})
# Prose test no. 9
@no_type_check
@async_client_context.require_version_min(4, 0, 0)
@async_client_context.require_version_max(4, 0, 7)
async def test_start_at_operation_time_caching(self):
# Case 1: change stream not started with startAtOperationTime
client, listener = self.client_with_listener("aggregate")
async with await self.change_stream_with_client(client) as cs:
await self.kill_change_stream_cursor(cs)
await cs.try_next()
cmd = listener.started_events[-1].command
self.assertIsNotNone(cmd["pipeline"][0]["$changeStream"].get("startAtOperationTime"))
# Case 2: change stream started with startAtOperationTime
listener.reset()
optime = await self.get_start_at_operation_time()
async with await self.change_stream_with_client(
client, start_at_operation_time=optime
) as cs:
await self.kill_change_stream_cursor(cs)
await cs.try_next()
cmd = listener.started_events[-1].command
self.assertEqual(
cmd["pipeline"][0]["$changeStream"].get("startAtOperationTime"),
optime,
str([k.command for k in listener.started_events]),
)
# Prose test no. 10 - SKIPPED
# This test is identical to prose test no. 3.
# Prose test no. 11
@no_type_check
@async_client_context.require_version_min(4, 0, 7)
@async_client_context.require_version_min(4, 2, 0)
async def test_resumetoken_empty_batch(self):
client, listener = await self._client_with_listener("getMore")
async with await self.change_stream_with_client(client) as change_stream:
@ -687,7 +648,7 @@ class ProseSpecTestsMixin:
# Prose test no. 11
@no_type_check
@async_client_context.require_version_min(4, 0, 7)
@async_client_context.require_version_min(4, 2, 0)
async def test_resumetoken_exhausted_batch(self):
client, listener = await self._client_with_listener("getMore")
async with await self.change_stream_with_client(client) as change_stream:
@ -697,38 +658,6 @@ class ProseSpecTestsMixin:
response = listener.succeeded_events[-1].reply
self.assertEqual(resume_token, response["cursor"]["postBatchResumeToken"])
# Prose test no. 12
@no_type_check
@async_client_context.require_version_max(4, 0, 7)
async def test_resumetoken_empty_batch_legacy(self):
resume_point = await self.get_resume_token()
# Empty resume token when neither resumeAfter or startAfter specified.
async with await self.change_stream() as change_stream:
await change_stream.try_next()
self.assertIsNone(change_stream.resume_token)
# Resume token value is same as resumeAfter.
async with await self.change_stream(resume_after=resume_point) as change_stream:
await change_stream.try_next()
resume_token = change_stream.resume_token
self.assertEqual(resume_token, resume_point)
# Prose test no. 12
@no_type_check
@async_client_context.require_version_max(4, 0, 7)
async def test_resumetoken_exhausted_batch_legacy(self):
# Resume token is _id of last change.
async with await self.change_stream() as change_stream:
change = await self._populate_and_exhaust_change_stream(change_stream)
self.assertEqual(change_stream.resume_token, change["_id"])
resume_point = change["_id"]
# Resume token is _id of last change even if resumeAfter is specified.
async with await self.change_stream(resume_after=resume_point) as change_stream:
change = await self._populate_and_exhaust_change_stream(change_stream)
self.assertEqual(change_stream.resume_token, change["_id"])
# Prose test no. 13
@no_type_check
async def test_resumetoken_partially_iterated_batch(self):
@ -770,13 +699,13 @@ class ProseSpecTestsMixin:
# Prose test no. 14
@no_type_check
@async_client_context.require_no_mongos
@async_client_context.require_version_min(4, 1, 1)
@async_client_context.require_version_min(4, 2, 0)
async def test_resumetoken_uniterated_nonempty_batch_startafter(self):
await self._test_resumetoken_uniterated_nonempty_batch("start_after")
# Prose test no. 17
@no_type_check
@async_client_context.require_version_min(4, 1, 1)
@async_client_context.require_version_min(4, 2, 0)
async def test_startafter_resume_uses_startafter_after_empty_getMore(self):
# Resume should use startAfter after no changes have been returned.
resume_point = await self.get_resume_token()
@ -796,7 +725,7 @@ class ProseSpecTestsMixin:
# Prose test no. 18
@no_type_check
@async_client_context.require_version_min(4, 1, 1)
@async_client_context.require_version_min(4, 2, 0)
async def test_startafter_resume_uses_resumeafter_after_nonempty_getMore(self):
# Resume should use resumeAfter after some changes have been returned.
resume_point = await self.get_resume_token()
@ -843,7 +772,7 @@ class ProseSpecTestsMixin:
class TestClusterAsyncChangeStream(TestAsyncChangeStreamBase, APITestsMixin):
dbs: list
@async_client_context.require_version_min(4, 0, 0, -1)
@async_client_context.require_version_min(4, 2, 0)
@async_client_context.require_change_streams
async def asyncSetUp(self) -> None:
await super().asyncSetUp()
@ -903,7 +832,7 @@ class TestClusterAsyncChangeStream(TestAsyncChangeStreamBase, APITestsMixin):
class TestAsyncDatabaseAsyncChangeStream(TestAsyncChangeStreamBase, APITestsMixin):
@async_client_context.require_version_min(4, 0, 0, -1)
@async_client_context.require_version_min(4, 2, 0)
@async_client_context.require_change_streams
async def asyncSetUp(self) -> None:
await super().asyncSetUp()

View File

@ -122,18 +122,12 @@ class TestAsyncConnectionsSurvivePrimaryStepDown(AsyncIntegrationTest):
async def test_not_primary_keep_connection_pool(self):
await self.run_scenario(10107, True, self.verify_pool_not_cleared)
@async_client_context.require_version_min(4, 0, 0)
@async_client_context.require_version_max(4, 1, 0, -1)
@async_client_context.require_test_commands
async def test_not_primary_reset_connection_pool(self):
await self.run_scenario(10107, False, self.verify_pool_cleared)
@async_client_context.require_version_min(4, 0, 0)
@async_client_context.require_version_min(4, 2, 0)
@async_client_context.require_test_commands
async def test_shutdown_in_progress(self):
await self.run_scenario(91, False, self.verify_pool_cleared)
@async_client_context.require_version_min(4, 0, 0)
@async_client_context.require_version_min(4, 2, 0)
@async_client_context.require_test_commands
async def test_interrupted_at_shutdown(self):
await self.run_scenario(11600, False, self.verify_pool_cleared)

View File

@ -1190,15 +1190,6 @@ class TestCursor(AsyncIntegrationTest):
self.assertEqual(["b", "c"], distinct)
@async_client_context.require_version_max(4, 1, 0, -1)
async def test_max_scan(self):
await self.db.drop_collection("test")
await self.db.test.insert_many([{} for _ in range(100)])
self.assertEqual(100, len(await self.db.test.find().to_list()))
self.assertEqual(50, len(await self.db.test.find().max_scan(50).to_list()))
self.assertEqual(50, len(await self.db.test.find().max_scan(90).max_scan(50).to_list()))
async def test_with_statement(self):
await self.db.drop_collection("test")
await self.db.test.insert_many([{} for _ in range(100)])
@ -1600,7 +1591,6 @@ class TestRawBatchCursor(AsyncIntegrationTest):
async def test_collation(self):
await anext(self.db.test.find_raw_batches(collation=Collation("en_US")))
@async_client_context.require_no_mmap # MMAPv1 does not support read concern
async def test_read_concern(self):
await self.db.get_collection("test", write_concern=WriteConcern(w="majority")).insert_one(
{}

View File

@ -953,7 +953,7 @@ class TestCollectionChangeStreamsWCustomTypes(
class TestDatabaseChangeStreamsWCustomTypes(
AsyncIntegrationTest, ChangeStreamsWCustomTypesTestMixin
):
@async_client_context.require_version_min(4, 0, 0)
@async_client_context.require_version_min(4, 2, 0)
@async_client_context.require_change_streams
async def asyncSetUp(self):
await super().asyncSetUp()
@ -973,7 +973,7 @@ class TestDatabaseChangeStreamsWCustomTypes(
class TestClusterChangeStreamsWCustomTypes(
AsyncIntegrationTest, ChangeStreamsWCustomTypesTestMixin
):
@async_client_context.require_version_min(4, 0, 0)
@async_client_context.require_version_min(4, 2, 0)
@async_client_context.require_change_streams
async def asyncSetUp(self):
await super().asyncSetUp()

View File

@ -451,20 +451,6 @@ class TestClientMaxWireVersion(AsyncIntegrationTest):
async def asyncSetUp(self):
await super().asyncSetUp()
@async_client_context.require_version_max(4, 0, 99)
async def test_raise_max_wire_version_error(self):
opts = AutoEncryptionOpts(KMS_PROVIDERS, "keyvault.datakeys")
client = await self.async_rs_or_single_client(auto_encryption_opts=opts)
msg = "Auto-encryption requires a minimum MongoDB version of 4.2"
with self.assertRaisesRegex(ConfigurationError, msg):
await client.test.test.insert_one({})
with self.assertRaisesRegex(ConfigurationError, msg):
await client.admin.command("ping")
with self.assertRaisesRegex(ConfigurationError, msg):
await client.test.test.find_one({})
with self.assertRaisesRegex(ConfigurationError, msg):
await client.test.test.bulk_write([InsertOne({})])
async def test_raise_unsupported_error(self):
opts = AutoEncryptionOpts(KMS_PROVIDERS, "keyvault.datakeys")
client = await self.async_rs_or_single_client(auto_encryption_opts=opts)

View File

@ -1162,7 +1162,6 @@ class TestTransactionExamples(AsyncIntegrationTest):
class TestCausalConsistencyExamples(AsyncIntegrationTest):
@async_client_context.require_secondaries_count(1)
@async_client_context.require_no_mmap
async def test_causal_consistency(self):
# Causal consistency examples
client = self.client

View File

@ -140,40 +140,10 @@ class IgnoreDeprecationsTest(AsyncIntegrationTest):
self.deprecation_filter.stop()
class TestRetryableWritesMMAPv1(IgnoreDeprecationsTest):
knobs: client_knobs
async def asyncSetUp(self) -> None:
await super().asyncSetUp()
# Speed up the tests by decreasing the heartbeat frequency.
self.knobs = client_knobs(heartbeat_frequency=0.1, min_heartbeat_interval=0.1)
self.knobs.enable()
self.client = await self.async_rs_or_single_client(retryWrites=True)
self.db = self.client.pymongo_test
async def asyncTearDown(self) -> None:
self.knobs.disable()
@async_client_context.require_no_standalone
async def test_actionable_error_message(self):
if async_client_context.storage_engine != "mmapv1":
raise SkipTest("This cluster is not running MMAPv1")
expected_msg = (
"This MongoDB deployment does not support retryable "
"writes. Please add retryWrites=false to your "
"connection string."
)
for method, args, kwargs in retryable_single_statement_ops(self.db.retryable_write_test):
with self.assertRaisesRegex(OperationFailure, expected_msg):
await method(*args, **kwargs)
class TestRetryableWrites(IgnoreDeprecationsTest):
listener: OvertCommandListener
knobs: client_knobs
@async_client_context.require_no_mmap
async def asyncSetUp(self) -> None:
await super().asyncSetUp()
# Speed up the tests by decreasing the heartbeat frequency.
@ -425,7 +395,6 @@ class TestWriteConcernError(AsyncIntegrationTest):
fail_insert: dict
@async_client_context.require_replica_set
@async_client_context.require_no_mmap
@async_client_context.require_failCommand_fail_point
async def asyncSetUp(self) -> None:
await super().asyncSetUp()
@ -596,7 +565,6 @@ class TestPoolPausedError(AsyncIntegrationTest):
# TODO: Make this a real integration test where we stepdown the primary.
class TestRetryableWritesTxnNumber(IgnoreDeprecationsTest):
@async_client_context.require_replica_set
@async_client_context.require_no_mmap
async def test_increment_transaction_id_without_sending_command(self):
"""Test that the txnNumber field is properly incremented, even when
the first attempt fails before sending the command.

View File

@ -1045,14 +1045,6 @@ class TestCausalConsistency(AsyncUnitTest):
lambda coll, session: coll.find({}, session=session).explain()
)
@async_client_context.require_no_standalone
@async_client_context.require_version_max(4, 1, 0)
async def test_aggregate_out_does_not_include_read_concern(self):
async def alambda(coll, session):
await (await coll.aggregate([{"$out": "aggout"}], session=session)).to_list()
await self._test_no_read_concern(alambda)
@async_client_context.require_no_standalone
async def test_get_more_does_not_include_read_concern(self):
coll = self.client.pymongo_test.test
@ -1095,7 +1087,6 @@ class TestCausalConsistency(AsyncUnitTest):
self.assertIsNone(act)
@async_client_context.require_no_standalone
@async_client_context.require_no_mmap
async def test_read_concern(self):
async with self.client.start_session(causal_consistency=True) as s:
coll = self.client.pymongo_test.test

View File

@ -27,7 +27,6 @@ from test.asynchronous.unified_format import generate_test_classes
_IS_SYNC = False
@client_context.require_no_mmap
def setUpModule():
pass

View File

@ -493,11 +493,6 @@ class UnifiedSpecTestMixinV1(AsyncIntegrationTest):
raise unittest.SkipTest(f"{self.__class__.__name__} runOnRequirements not satisfied")
# add any special-casing for skipping tests here
if async_client_context.storage_engine == "mmapv1":
if "retryable-writes" in self.TEST_SPEC["description"] or "retryable_writes" in str(
self.TEST_PATH
):
raise unittest.SkipTest("MMAPv1 does not support retryWrites=True")
# Handle mongos_clients for transactions tests.
self.mongos_clients = []
@ -519,13 +514,6 @@ class UnifiedSpecTestMixinV1(AsyncIntegrationTest):
def maybe_skip_test(self, spec):
# add any special-casing for skipping tests here
if async_client_context.storage_engine == "mmapv1":
if (
"Dirty explicit session is discarded" in spec["description"]
or "Dirty implicit session is discarded" in spec["description"]
or "Cancel server check" in spec["description"]
):
self.skipTest("MMAPv1 does not support retryWrites=True")
if "Client side error in command starting transaction" in spec["description"]:
self.skipTest("Implement PYTHON-1894")
if "timeoutMS applied to entire download" in spec["description"]:
@ -544,10 +532,6 @@ class UnifiedSpecTestMixinV1(AsyncIntegrationTest):
if "csot" in class_name:
if "gridfs" in class_name and sys.platform == "win32":
self.skipTest("PYTHON-3522 CSOT GridFS tests are flaky on Windows")
if async_client_context.storage_engine == "mmapv1":
self.skipTest(
"MMAPv1 does not support retryable writes which is required for CSOT tests"
)
if "change" in description or "change" in class_name:
self.skipTest("CSOT not implemented for watch()")
if "cursors" in class_name:
@ -572,11 +556,6 @@ class UnifiedSpecTestMixinV1(AsyncIntegrationTest):
self.skipTest("PyMongo does not support count()")
if name == "listIndexNames":
self.skipTest("PyMongo does not support list_index_names()")
if async_client_context.storage_engine == "mmapv1":
if name == "createChangeStream":
self.skipTest("MMAPv1 does not support change streams")
if name == "withTransaction" or name == "startTransaction":
self.skipTest("MMAPv1 does not support document-level locking")
if not async_client_context.test_commands_enabled:
if name == "failPoint" or name == "targetedFailPoint":
self.skipTest("Test commands must be enabled to use fail points")
@ -682,8 +661,6 @@ class UnifiedSpecTestMixinV1(AsyncIntegrationTest):
self.fail(f"Operation {opname} not supported for entity of type {type(target)}")
async def __entityOperation_createChangeStream(self, target, *args, **kwargs):
if async_client_context.storage_engine == "mmapv1":
self.skipTest("MMAPv1 does not support change streams")
self.__raise_if_unsupported(
"createChangeStream", target, AsyncMongoClient, AsyncDatabase, AsyncCollection
)
@ -810,14 +787,10 @@ class UnifiedSpecTestMixinV1(AsyncIntegrationTest):
return await (await target.list_search_indexes(name, **agg_kwargs)).to_list()
async def _sessionOperation_withTransaction(self, target, *args, **kwargs):
if async_client_context.storage_engine == "mmapv1":
self.skipTest("MMAPv1 does not support document-level locking")
self.__raise_if_unsupported("withTransaction", target, AsyncClientSession)
return await target.with_transaction(*args, **kwargs)
async def _sessionOperation_startTransaction(self, target, *args, **kwargs):
if async_client_context.storage_engine == "mmapv1":
self.skipTest("MMAPv1 does not support document-level locking")
self.__raise_if_unsupported("startTransaction", target, AsyncClientSession)
return await target.start_transaction(*args, **kwargs)

View File

@ -648,12 +648,6 @@ class AsyncSpecRunner(AsyncIntegrationTest):
server_listener = ServerAndTopologyEventListener()
# Create a new client, to avoid interference from pooled sessions.
client_options = self.parse_client_options(test["clientOptions"])
# MMAPv1 does not support retryable writes.
if (
client_options.get("retryWrites") is True
and async_client_context.storage_engine == "mmapv1"
):
self.skipTest("MMAPv1 does not support retryWrites=True")
use_multi_mongos = test["useMultipleMongoses"]
host = None
if use_multi_mongos:

View File

@ -1,70 +0,0 @@
{
"description": "Pre-4.2 InterruptedAtShutdown error",
"uri": "mongodb://a/?replicaSet=rs",
"phases": [
{
"description": "Primary A is discovered",
"responses": [
[
"a:27017",
{
"ok": 1,
"helloOk": true,
"isWritablePrimary": true,
"hosts": [
"a:27017"
],
"setName": "rs",
"minWireVersion": 0,
"maxWireVersion": 7
}
]
],
"outcome": {
"servers": {
"a:27017": {
"type": "RSPrimary",
"setName": "rs",
"topologyVersion": null,
"pool": {
"generation": 0
}
}
},
"topologyType": "ReplicaSetWithPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
},
{
"description": "Pre-4.2 InterruptedAtShutdown error marks server Unknown and clears the pool",
"applicationErrors": [
{
"address": "a:27017",
"when": "afterHandshakeCompletes",
"maxWireVersion": 7,
"type": "command",
"response": {
"ok": 0,
"errmsg": "InterruptedAtShutdown",
"code": 11600
}
}
],
"outcome": {
"servers": {
"a:27017": {
"type": "Unknown",
"topologyVersion": null,
"pool": {
"generation": 1
}
}
},
"topologyType": "ReplicaSetNoPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
}
]
}

View File

@ -1,70 +0,0 @@
{
"description": "Pre-4.2 InterruptedDueToReplStateChange error",
"uri": "mongodb://a/?replicaSet=rs",
"phases": [
{
"description": "Primary A is discovered",
"responses": [
[
"a:27017",
{
"ok": 1,
"helloOk": true,
"isWritablePrimary": true,
"hosts": [
"a:27017"
],
"setName": "rs",
"minWireVersion": 0,
"maxWireVersion": 7
}
]
],
"outcome": {
"servers": {
"a:27017": {
"type": "RSPrimary",
"setName": "rs",
"topologyVersion": null,
"pool": {
"generation": 0
}
}
},
"topologyType": "ReplicaSetWithPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
},
{
"description": "Pre-4.2 InterruptedDueToReplStateChange error marks server Unknown and clears the pool",
"applicationErrors": [
{
"address": "a:27017",
"when": "afterHandshakeCompletes",
"maxWireVersion": 7,
"type": "command",
"response": {
"ok": 0,
"errmsg": "InterruptedDueToReplStateChange",
"code": 11602
}
}
],
"outcome": {
"servers": {
"a:27017": {
"type": "Unknown",
"topologyVersion": null,
"pool": {
"generation": 1
}
}
},
"topologyType": "ReplicaSetNoPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
}
]
}

View File

@ -1,70 +0,0 @@
{
"description": "Pre-4.2 LegacyNotPrimary error",
"uri": "mongodb://a/?replicaSet=rs",
"phases": [
{
"description": "Primary A is discovered",
"responses": [
[
"a:27017",
{
"ok": 1,
"helloOk": true,
"isWritablePrimary": true,
"hosts": [
"a:27017"
],
"setName": "rs",
"minWireVersion": 0,
"maxWireVersion": 7
}
]
],
"outcome": {
"servers": {
"a:27017": {
"type": "RSPrimary",
"setName": "rs",
"topologyVersion": null,
"pool": {
"generation": 0
}
}
},
"topologyType": "ReplicaSetWithPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
},
{
"description": "Pre-4.2 LegacyNotPrimary error marks server Unknown and clears the pool",
"applicationErrors": [
{
"address": "a:27017",
"when": "afterHandshakeCompletes",
"maxWireVersion": 7,
"type": "command",
"response": {
"ok": 0,
"errmsg": "LegacyNotPrimary",
"code": 10058
}
}
],
"outcome": {
"servers": {
"a:27017": {
"type": "Unknown",
"topologyVersion": null,
"pool": {
"generation": 1
}
}
},
"topologyType": "ReplicaSetNoPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
}
]
}

View File

@ -1,70 +0,0 @@
{
"description": "Pre-4.2 NotPrimaryNoSecondaryOk error",
"uri": "mongodb://a/?replicaSet=rs",
"phases": [
{
"description": "Primary A is discovered",
"responses": [
[
"a:27017",
{
"ok": 1,
"helloOk": true,
"isWritablePrimary": true,
"hosts": [
"a:27017"
],
"setName": "rs",
"minWireVersion": 0,
"maxWireVersion": 7
}
]
],
"outcome": {
"servers": {
"a:27017": {
"type": "RSPrimary",
"setName": "rs",
"topologyVersion": null,
"pool": {
"generation": 0
}
}
},
"topologyType": "ReplicaSetWithPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
},
{
"description": "Pre-4.2 NotPrimaryNoSecondaryOk error marks server Unknown and clears the pool",
"applicationErrors": [
{
"address": "a:27017",
"when": "afterHandshakeCompletes",
"maxWireVersion": 7,
"type": "command",
"response": {
"ok": 0,
"errmsg": "NotPrimaryNoSecondaryOk",
"code": 13435
}
}
],
"outcome": {
"servers": {
"a:27017": {
"type": "Unknown",
"topologyVersion": null,
"pool": {
"generation": 1
}
}
},
"topologyType": "ReplicaSetNoPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
}
]
}

View File

@ -1,70 +0,0 @@
{
"description": "Pre-4.2 NotPrimaryOrSecondary error",
"uri": "mongodb://a/?replicaSet=rs",
"phases": [
{
"description": "Primary A is discovered",
"responses": [
[
"a:27017",
{
"ok": 1,
"helloOk": true,
"isWritablePrimary": true,
"hosts": [
"a:27017"
],
"setName": "rs",
"minWireVersion": 0,
"maxWireVersion": 7
}
]
],
"outcome": {
"servers": {
"a:27017": {
"type": "RSPrimary",
"setName": "rs",
"topologyVersion": null,
"pool": {
"generation": 0
}
}
},
"topologyType": "ReplicaSetWithPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
},
{
"description": "Pre-4.2 NotPrimaryOrSecondary error marks server Unknown and clears the pool",
"applicationErrors": [
{
"address": "a:27017",
"when": "afterHandshakeCompletes",
"maxWireVersion": 7,
"type": "command",
"response": {
"ok": 0,
"errmsg": "NotPrimaryOrSecondary",
"code": 13436
}
}
],
"outcome": {
"servers": {
"a:27017": {
"type": "Unknown",
"topologyVersion": null,
"pool": {
"generation": 1
}
}
},
"topologyType": "ReplicaSetNoPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
}
]
}

View File

@ -1,70 +0,0 @@
{
"description": "Pre-4.2 NotWritablePrimary error",
"uri": "mongodb://a/?replicaSet=rs",
"phases": [
{
"description": "Primary A is discovered",
"responses": [
[
"a:27017",
{
"ok": 1,
"helloOk": true,
"isWritablePrimary": true,
"hosts": [
"a:27017"
],
"setName": "rs",
"minWireVersion": 0,
"maxWireVersion": 7
}
]
],
"outcome": {
"servers": {
"a:27017": {
"type": "RSPrimary",
"setName": "rs",
"topologyVersion": null,
"pool": {
"generation": 0
}
}
},
"topologyType": "ReplicaSetWithPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
},
{
"description": "Pre-4.2 NotWritablePrimary error marks server Unknown and clears the pool",
"applicationErrors": [
{
"address": "a:27017",
"when": "afterHandshakeCompletes",
"maxWireVersion": 7,
"type": "command",
"response": {
"ok": 0,
"errmsg": "NotWritablePrimary",
"code": 10107
}
}
],
"outcome": {
"servers": {
"a:27017": {
"type": "Unknown",
"topologyVersion": null,
"pool": {
"generation": 1
}
}
},
"topologyType": "ReplicaSetNoPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
}
]
}

View File

@ -1,70 +0,0 @@
{
"description": "Pre-4.2 PrimarySteppedDown error",
"uri": "mongodb://a/?replicaSet=rs",
"phases": [
{
"description": "Primary A is discovered",
"responses": [
[
"a:27017",
{
"ok": 1,
"helloOk": true,
"isWritablePrimary": true,
"hosts": [
"a:27017"
],
"setName": "rs",
"minWireVersion": 0,
"maxWireVersion": 7
}
]
],
"outcome": {
"servers": {
"a:27017": {
"type": "RSPrimary",
"setName": "rs",
"topologyVersion": null,
"pool": {
"generation": 0
}
}
},
"topologyType": "ReplicaSetWithPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
},
{
"description": "Pre-4.2 PrimarySteppedDown error marks server Unknown and clears the pool",
"applicationErrors": [
{
"address": "a:27017",
"when": "afterHandshakeCompletes",
"maxWireVersion": 7,
"type": "command",
"response": {
"ok": 0,
"errmsg": "PrimarySteppedDown",
"code": 189
}
}
],
"outcome": {
"servers": {
"a:27017": {
"type": "Unknown",
"topologyVersion": null,
"pool": {
"generation": 1
}
}
},
"topologyType": "ReplicaSetNoPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
}
]
}

View File

@ -1,70 +0,0 @@
{
"description": "Pre-4.2 ShutdownInProgress error",
"uri": "mongodb://a/?replicaSet=rs",
"phases": [
{
"description": "Primary A is discovered",
"responses": [
[
"a:27017",
{
"ok": 1,
"helloOk": true,
"isWritablePrimary": true,
"hosts": [
"a:27017"
],
"setName": "rs",
"minWireVersion": 0,
"maxWireVersion": 7
}
]
],
"outcome": {
"servers": {
"a:27017": {
"type": "RSPrimary",
"setName": "rs",
"topologyVersion": null,
"pool": {
"generation": 0
}
}
},
"topologyType": "ReplicaSetWithPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
},
{
"description": "Pre-4.2 ShutdownInProgress error marks server Unknown and clears the pool",
"applicationErrors": [
{
"address": "a:27017",
"when": "afterHandshakeCompletes",
"maxWireVersion": 7,
"type": "command",
"response": {
"ok": 0,
"errmsg": "ShutdownInProgress",
"code": 91
}
}
],
"outcome": {
"servers": {
"a:27017": {
"type": "Unknown",
"topologyVersion": null,
"pool": {
"generation": 1
}
}
},
"topologyType": "ReplicaSetNoPrimary",
"logicalSessionTimeoutMinutes": null,
"setName": "rs"
}
}
]
}

View File

@ -18,7 +18,7 @@
"setVersion": 1,
"setName": "rs",
"minWireVersion": 0,
"maxWireVersion": 7
"maxWireVersion": 16
}
]
],
@ -66,7 +66,7 @@
"$oid": "000000000000000000000002"
},
"minWireVersion": 0,
"maxWireVersion": 7
"maxWireVersion": 16
}
]
],
@ -116,7 +116,7 @@
"setVersion": 1,
"setName": "rs",
"minWireVersion": 0,
"maxWireVersion": 7
"maxWireVersion": 16
}
]
],
@ -167,7 +167,7 @@
"$oid": "000000000000000000000001"
},
"minWireVersion": 0,
"maxWireVersion": 7
"maxWireVersion": 16
}
]
],

View File

@ -18,7 +18,7 @@
"primary": "localhost:27017",
"me": "a:27017",
"minWireVersion": 0,
"maxWireVersion": 7
"maxWireVersion": 25
}
]
],
@ -55,7 +55,7 @@
"primary": "localhost:27017",
"me": "localhost:27018",
"minWireVersion": 0,
"maxWireVersion": 7
"maxWireVersion": 25
}
]
],

View File

@ -17,7 +17,7 @@
"setName": "rs",
"setVersion": 2,
"minWireVersion": 0,
"maxWireVersion": 7
"maxWireVersion": 16
}
]
],
@ -56,7 +56,7 @@
"setName": "rs",
"setVersion": 1,
"minWireVersion": 0,
"maxWireVersion": 7
"maxWireVersion": 16
}
]
],

View File

@ -20,7 +20,7 @@
"$oid": "000000000000000000000001"
},
"minWireVersion": 0,
"maxWireVersion": 7
"maxWireVersion": 16
}
]
],
@ -64,7 +64,7 @@
"setName": "rs",
"setVersion": 2,
"minWireVersion": 0,
"maxWireVersion": 7
"maxWireVersion": 16
}
]
],
@ -109,7 +109,7 @@
"$oid": "000000000000000000000002"
},
"minWireVersion": 0,
"maxWireVersion": 7
"maxWireVersion": 16
}
]
],

View File

@ -40,7 +40,7 @@ class TestCursorNamespace(PyMongoTestCase):
@classmethod
def setUpClass(cls):
cls.server = MockupDB(auto_ismaster={"maxWireVersion": 7})
cls.server = MockupDB(auto_ismaster={"maxWireVersion": 8})
cls.server.run()
cls.client = cls.unmanaged_simple_client(cls.server.uri)

View File

@ -165,7 +165,7 @@ class TestBulk(BulkTestBase):
def test_update_many(self):
self._test_update_many({"$set": {"foo": "bar"}})
@client_context.require_version_min(4, 1, 11)
@client_context.require_version_min(4, 2, 0)
def test_update_many_pipeline(self):
self._test_update_many([{"$set": {"foo": "bar"}}])
@ -206,7 +206,7 @@ class TestBulk(BulkTestBase):
def test_update_one(self):
self._test_update_one({"$set": {"foo": "bar"}})
@client_context.require_version_min(4, 1, 11)
@client_context.require_version_min(4, 2, 0)
def test_update_one_pipeline(self):
self._test_update_one([{"$set": {"foo": "bar"}}])

View File

@ -263,7 +263,7 @@ class APITestsMixin:
# $changeStream.startAtOperationTime was added in 4.0.0.
@no_type_check
@client_context.require_version_min(4, 0, 0)
@client_context.require_version_min(4, 2, 0)
def test_start_at_operation_time(self):
optime = self.get_start_at_operation_time()
@ -432,7 +432,7 @@ class APITestsMixin:
self._test_get_invalidate_event(change_stream)
@no_type_check
@client_context.require_version_min(4, 1, 1)
@client_context.require_version_min(4, 2, 0)
def test_start_after(self):
resume_token = self.get_resume_token(invalidate=True)
@ -448,7 +448,7 @@ class APITestsMixin:
self.assertEqual(change["fullDocument"], {"_id": 2})
@no_type_check
@client_context.require_version_min(4, 1, 1)
@client_context.require_version_min(4, 2, 0)
def test_start_after_resume_process_with_changes(self):
resume_token = self.get_resume_token(invalidate=True)
@ -553,27 +553,16 @@ class ProseSpecTestsMixin:
)
# Prose test no. 1
@client_context.require_version_min(4, 0, 7)
@client_context.require_version_min(4, 2, 0)
def test_update_resume_token(self):
self._test_update_resume_token(self._get_expected_resume_token)
# Prose test no. 1
@client_context.require_version_max(4, 0, 7)
def test_update_resume_token_legacy(self):
self._test_update_resume_token(self._get_expected_resume_token_legacy)
# Prose test no. 2
@client_context.require_version_min(4, 1, 8)
@client_context.require_version_min(4, 2, 0)
def test_raises_error_on_missing_id_418plus(self):
# Server returns an error on 4.1.8+
self._test_raises_error_on_missing_id(OperationFailure)
# Prose test no. 2
@client_context.require_version_max(4, 1, 8)
def test_raises_error_on_missing_id_418minus(self):
# PyMongo raises an error
self._test_raises_error_on_missing_id(InvalidOperation)
# Prose test no. 3
@no_type_check
def test_resume_on_error(self):
@ -632,38 +621,12 @@ class ProseSpecTestsMixin:
cursor.close = raise_error
self.insert_one_and_check(change_stream, {"_id": 2})
# Prose test no. 9
@no_type_check
@client_context.require_version_min(4, 0, 0)
@client_context.require_version_max(4, 0, 7)
def test_start_at_operation_time_caching(self):
# Case 1: change stream not started with startAtOperationTime
client, listener = self.client_with_listener("aggregate")
with self.change_stream_with_client(client) as cs:
self.kill_change_stream_cursor(cs)
cs.try_next()
cmd = listener.started_events[-1].command
self.assertIsNotNone(cmd["pipeline"][0]["$changeStream"].get("startAtOperationTime"))
# Case 2: change stream started with startAtOperationTime
listener.reset()
optime = self.get_start_at_operation_time()
with self.change_stream_with_client(client, start_at_operation_time=optime) as cs:
self.kill_change_stream_cursor(cs)
cs.try_next()
cmd = listener.started_events[-1].command
self.assertEqual(
cmd["pipeline"][0]["$changeStream"].get("startAtOperationTime"),
optime,
str([k.command for k in listener.started_events]),
)
# Prose test no. 10 - SKIPPED
# This test is identical to prose test no. 3.
# Prose test no. 11
@no_type_check
@client_context.require_version_min(4, 0, 7)
@client_context.require_version_min(4, 2, 0)
def test_resumetoken_empty_batch(self):
client, listener = self._client_with_listener("getMore")
with self.change_stream_with_client(client) as change_stream:
@ -675,7 +638,7 @@ class ProseSpecTestsMixin:
# Prose test no. 11
@no_type_check
@client_context.require_version_min(4, 0, 7)
@client_context.require_version_min(4, 2, 0)
def test_resumetoken_exhausted_batch(self):
client, listener = self._client_with_listener("getMore")
with self.change_stream_with_client(client) as change_stream:
@ -685,38 +648,6 @@ class ProseSpecTestsMixin:
response = listener.succeeded_events[-1].reply
self.assertEqual(resume_token, response["cursor"]["postBatchResumeToken"])
# Prose test no. 12
@no_type_check
@client_context.require_version_max(4, 0, 7)
def test_resumetoken_empty_batch_legacy(self):
resume_point = self.get_resume_token()
# Empty resume token when neither resumeAfter or startAfter specified.
with self.change_stream() as change_stream:
change_stream.try_next()
self.assertIsNone(change_stream.resume_token)
# Resume token value is same as resumeAfter.
with self.change_stream(resume_after=resume_point) as change_stream:
change_stream.try_next()
resume_token = change_stream.resume_token
self.assertEqual(resume_token, resume_point)
# Prose test no. 12
@no_type_check
@client_context.require_version_max(4, 0, 7)
def test_resumetoken_exhausted_batch_legacy(self):
# Resume token is _id of last change.
with self.change_stream() as change_stream:
change = self._populate_and_exhaust_change_stream(change_stream)
self.assertEqual(change_stream.resume_token, change["_id"])
resume_point = change["_id"]
# Resume token is _id of last change even if resumeAfter is specified.
with self.change_stream(resume_after=resume_point) as change_stream:
change = self._populate_and_exhaust_change_stream(change_stream)
self.assertEqual(change_stream.resume_token, change["_id"])
# Prose test no. 13
@no_type_check
def test_resumetoken_partially_iterated_batch(self):
@ -758,13 +689,13 @@ class ProseSpecTestsMixin:
# Prose test no. 14
@no_type_check
@client_context.require_no_mongos
@client_context.require_version_min(4, 1, 1)
@client_context.require_version_min(4, 2, 0)
def test_resumetoken_uniterated_nonempty_batch_startafter(self):
self._test_resumetoken_uniterated_nonempty_batch("start_after")
# Prose test no. 17
@no_type_check
@client_context.require_version_min(4, 1, 1)
@client_context.require_version_min(4, 2, 0)
def test_startafter_resume_uses_startafter_after_empty_getMore(self):
# Resume should use startAfter after no changes have been returned.
resume_point = self.get_resume_token()
@ -782,7 +713,7 @@ class ProseSpecTestsMixin:
# Prose test no. 18
@no_type_check
@client_context.require_version_min(4, 1, 1)
@client_context.require_version_min(4, 2, 0)
def test_startafter_resume_uses_resumeafter_after_nonempty_getMore(self):
# Resume should use resumeAfter after some changes have been returned.
resume_point = self.get_resume_token()
@ -827,7 +758,7 @@ class ProseSpecTestsMixin:
class TestClusterChangeStream(TestChangeStreamBase, APITestsMixin):
dbs: list
@client_context.require_version_min(4, 0, 0, -1)
@client_context.require_version_min(4, 2, 0)
@client_context.require_change_streams
def setUp(self) -> None:
super().setUp()
@ -887,7 +818,7 @@ class TestClusterChangeStream(TestChangeStreamBase, APITestsMixin):
class TestDatabaseChangeStream(TestChangeStreamBase, APITestsMixin):
@client_context.require_version_min(4, 0, 0, -1)
@client_context.require_version_min(4, 2, 0)
@client_context.require_change_streams
def setUp(self) -> None:
super().setUp()

View File

@ -122,18 +122,12 @@ class TestConnectionsSurvivePrimaryStepDown(IntegrationTest):
def test_not_primary_keep_connection_pool(self):
self.run_scenario(10107, True, self.verify_pool_not_cleared)
@client_context.require_version_min(4, 0, 0)
@client_context.require_version_max(4, 1, 0, -1)
@client_context.require_test_commands
def test_not_primary_reset_connection_pool(self):
self.run_scenario(10107, False, self.verify_pool_cleared)
@client_context.require_version_min(4, 0, 0)
@client_context.require_version_min(4, 2, 0)
@client_context.require_test_commands
def test_shutdown_in_progress(self):
self.run_scenario(91, False, self.verify_pool_cleared)
@client_context.require_version_min(4, 0, 0)
@client_context.require_version_min(4, 2, 0)
@client_context.require_test_commands
def test_interrupted_at_shutdown(self):
self.run_scenario(11600, False, self.verify_pool_cleared)

View File

@ -1181,15 +1181,6 @@ class TestCursor(IntegrationTest):
self.assertEqual(["b", "c"], distinct)
@client_context.require_version_max(4, 1, 0, -1)
def test_max_scan(self):
self.db.drop_collection("test")
self.db.test.insert_many([{} for _ in range(100)])
self.assertEqual(100, len(self.db.test.find().to_list()))
self.assertEqual(50, len(self.db.test.find().max_scan(50).to_list()))
self.assertEqual(50, len(self.db.test.find().max_scan(90).max_scan(50).to_list()))
def test_with_statement(self):
self.db.drop_collection("test")
self.db.test.insert_many([{} for _ in range(100)])
@ -1591,7 +1582,6 @@ class TestRawBatchCursor(IntegrationTest):
def test_collation(self):
next(self.db.test.find_raw_batches(collation=Collation("en_US")))
@client_context.require_no_mmap # MMAPv1 does not support read concern
def test_read_concern(self):
self.db.get_collection("test", write_concern=WriteConcern(w="majority")).insert_one({})
c = self.db.get_collection("test", read_concern=ReadConcern("majority"))

View File

@ -949,7 +949,7 @@ class TestCollectionChangeStreamsWCustomTypes(IntegrationTest, ChangeStreamsWCus
class TestDatabaseChangeStreamsWCustomTypes(IntegrationTest, ChangeStreamsWCustomTypesTestMixin):
@client_context.require_version_min(4, 0, 0)
@client_context.require_version_min(4, 2, 0)
@client_context.require_change_streams
def setUp(self):
super().setUp()
@ -967,7 +967,7 @@ class TestDatabaseChangeStreamsWCustomTypes(IntegrationTest, ChangeStreamsWCusto
class TestClusterChangeStreamsWCustomTypes(IntegrationTest, ChangeStreamsWCustomTypesTestMixin):
@client_context.require_version_min(4, 0, 0)
@client_context.require_version_min(4, 2, 0)
@client_context.require_change_streams
def setUp(self):
super().setUp()

View File

@ -451,20 +451,6 @@ class TestClientMaxWireVersion(IntegrationTest):
def setUp(self):
super().setUp()
@client_context.require_version_max(4, 0, 99)
def test_raise_max_wire_version_error(self):
opts = AutoEncryptionOpts(KMS_PROVIDERS, "keyvault.datakeys")
client = self.rs_or_single_client(auto_encryption_opts=opts)
msg = "Auto-encryption requires a minimum MongoDB version of 4.2"
with self.assertRaisesRegex(ConfigurationError, msg):
client.test.test.insert_one({})
with self.assertRaisesRegex(ConfigurationError, msg):
client.admin.command("ping")
with self.assertRaisesRegex(ConfigurationError, msg):
client.test.test.find_one({})
with self.assertRaisesRegex(ConfigurationError, msg):
client.test.test.bulk_write([InsertOne({})])
def test_raise_unsupported_error(self):
opts = AutoEncryptionOpts(KMS_PROVIDERS, "keyvault.datakeys")
client = self.rs_or_single_client(auto_encryption_opts=opts)

View File

@ -1160,7 +1160,6 @@ class TestTransactionExamples(IntegrationTest):
class TestCausalConsistencyExamples(IntegrationTest):
@client_context.require_secondaries_count(1)
@client_context.require_no_mmap
def test_causal_consistency(self):
# Causal consistency examples
client = self.client

View File

@ -140,40 +140,10 @@ class IgnoreDeprecationsTest(IntegrationTest):
self.deprecation_filter.stop()
class TestRetryableWritesMMAPv1(IgnoreDeprecationsTest):
knobs: client_knobs
def setUp(self) -> None:
super().setUp()
# Speed up the tests by decreasing the heartbeat frequency.
self.knobs = client_knobs(heartbeat_frequency=0.1, min_heartbeat_interval=0.1)
self.knobs.enable()
self.client = self.rs_or_single_client(retryWrites=True)
self.db = self.client.pymongo_test
def tearDown(self) -> None:
self.knobs.disable()
@client_context.require_no_standalone
def test_actionable_error_message(self):
if client_context.storage_engine != "mmapv1":
raise SkipTest("This cluster is not running MMAPv1")
expected_msg = (
"This MongoDB deployment does not support retryable "
"writes. Please add retryWrites=false to your "
"connection string."
)
for method, args, kwargs in retryable_single_statement_ops(self.db.retryable_write_test):
with self.assertRaisesRegex(OperationFailure, expected_msg):
method(*args, **kwargs)
class TestRetryableWrites(IgnoreDeprecationsTest):
listener: OvertCommandListener
knobs: client_knobs
@client_context.require_no_mmap
def setUp(self) -> None:
super().setUp()
# Speed up the tests by decreasing the heartbeat frequency.
@ -423,7 +393,6 @@ class TestWriteConcernError(IntegrationTest):
fail_insert: dict
@client_context.require_replica_set
@client_context.require_no_mmap
@client_context.require_failCommand_fail_point
def setUp(self) -> None:
super().setUp()
@ -592,7 +561,6 @@ class TestPoolPausedError(IntegrationTest):
# TODO: Make this a real integration test where we stepdown the primary.
class TestRetryableWritesTxnNumber(IgnoreDeprecationsTest):
@client_context.require_replica_set
@client_context.require_no_mmap
def test_increment_transaction_id_without_sending_command(self):
"""Test that the txnNumber field is properly incremented, even when
the first attempt fails before sending the command.

View File

@ -1031,14 +1031,6 @@ class TestCausalConsistency(UnitTest):
# Not a write, but explain also doesn't support readConcern.
self._test_no_read_concern(lambda coll, session: coll.find({}, session=session).explain())
@client_context.require_no_standalone
@client_context.require_version_max(4, 1, 0)
def test_aggregate_out_does_not_include_read_concern(self):
def alambda(coll, session):
(coll.aggregate([{"$out": "aggout"}], session=session)).to_list()
self._test_no_read_concern(alambda)
@client_context.require_no_standalone
def test_get_more_does_not_include_read_concern(self):
coll = self.client.pymongo_test.test
@ -1081,7 +1073,6 @@ class TestCausalConsistency(UnitTest):
self.assertIsNone(act)
@client_context.require_no_standalone
@client_context.require_no_mmap
def test_read_concern(self):
with self.client.start_session(causal_consistency=True) as s:
coll = self.client.pymongo_test.test

View File

@ -559,7 +559,7 @@ class TestMultiServerTopology(TopologyTest):
)
self.assertEqual(server.description.min_wire_version, 1)
self.assertEqual(server.description.max_wire_version, 7)
self.assertEqual(server.description.max_wire_version, 8)
t.select_servers(any_server_selector, _Op.TEST)
# Incompatible.

View File

@ -27,7 +27,6 @@ from test.unified_format import generate_test_classes
_IS_SYNC = True
@client_context.require_no_mmap
def setUpModule():
pass

View File

@ -492,11 +492,6 @@ class UnifiedSpecTestMixinV1(IntegrationTest):
raise unittest.SkipTest(f"{self.__class__.__name__} runOnRequirements not satisfied")
# add any special-casing for skipping tests here
if client_context.storage_engine == "mmapv1":
if "retryable-writes" in self.TEST_SPEC["description"] or "retryable_writes" in str(
self.TEST_PATH
):
raise unittest.SkipTest("MMAPv1 does not support retryWrites=True")
# Handle mongos_clients for transactions tests.
self.mongos_clients = []
@ -518,13 +513,6 @@ class UnifiedSpecTestMixinV1(IntegrationTest):
def maybe_skip_test(self, spec):
# add any special-casing for skipping tests here
if client_context.storage_engine == "mmapv1":
if (
"Dirty explicit session is discarded" in spec["description"]
or "Dirty implicit session is discarded" in spec["description"]
or "Cancel server check" in spec["description"]
):
self.skipTest("MMAPv1 does not support retryWrites=True")
if "Client side error in command starting transaction" in spec["description"]:
self.skipTest("Implement PYTHON-1894")
if "timeoutMS applied to entire download" in spec["description"]:
@ -543,10 +531,6 @@ class UnifiedSpecTestMixinV1(IntegrationTest):
if "csot" in class_name:
if "gridfs" in class_name and sys.platform == "win32":
self.skipTest("PYTHON-3522 CSOT GridFS tests are flaky on Windows")
if client_context.storage_engine == "mmapv1":
self.skipTest(
"MMAPv1 does not support retryable writes which is required for CSOT tests"
)
if "change" in description or "change" in class_name:
self.skipTest("CSOT not implemented for watch()")
if "cursors" in class_name:
@ -571,11 +555,6 @@ class UnifiedSpecTestMixinV1(IntegrationTest):
self.skipTest("PyMongo does not support count()")
if name == "listIndexNames":
self.skipTest("PyMongo does not support list_index_names()")
if client_context.storage_engine == "mmapv1":
if name == "createChangeStream":
self.skipTest("MMAPv1 does not support change streams")
if name == "withTransaction" or name == "startTransaction":
self.skipTest("MMAPv1 does not support document-level locking")
if not client_context.test_commands_enabled:
if name == "failPoint" or name == "targetedFailPoint":
self.skipTest("Test commands must be enabled to use fail points")
@ -681,8 +660,6 @@ class UnifiedSpecTestMixinV1(IntegrationTest):
self.fail(f"Operation {opname} not supported for entity of type {type(target)}")
def __entityOperation_createChangeStream(self, target, *args, **kwargs):
if client_context.storage_engine == "mmapv1":
self.skipTest("MMAPv1 does not support change streams")
self.__raise_if_unsupported("createChangeStream", target, MongoClient, Database, Collection)
stream = target.watch(*args, **kwargs)
self.addCleanup(stream.close)
@ -807,14 +784,10 @@ class UnifiedSpecTestMixinV1(IntegrationTest):
return (target.list_search_indexes(name, **agg_kwargs)).to_list()
def _sessionOperation_withTransaction(self, target, *args, **kwargs):
if client_context.storage_engine == "mmapv1":
self.skipTest("MMAPv1 does not support document-level locking")
self.__raise_if_unsupported("withTransaction", target, ClientSession)
return target.with_transaction(*args, **kwargs)
def _sessionOperation_startTransaction(self, target, *args, **kwargs):
if client_context.storage_engine == "mmapv1":
self.skipTest("MMAPv1 does not support document-level locking")
self.__raise_if_unsupported("startTransaction", target, ClientSession)
return target.start_transaction(*args, **kwargs)

View File

@ -648,9 +648,6 @@ class SpecRunner(IntegrationTest):
server_listener = ServerAndTopologyEventListener()
# Create a new client, to avoid interference from pooled sessions.
client_options = self.parse_client_options(test["clientOptions"])
# MMAPv1 does not support retryable writes.
if client_options.get("retryWrites") is True and client_context.storage_engine == "mmapv1":
self.skipTest("MMAPv1 does not support retryWrites=True")
use_multi_mongos = test["useMultipleMongoses"]
host = None
if use_multi_mongos: