From 214caa558a14377ab4a120c65fee81e8477d7191 Mon Sep 17 00:00:00 2001 From: Iris Ho Date: Thu, 30 Jan 2025 15:46:26 -0800 Subject: [PATCH 1/3] Convert test.test_max_staleness to async --- test/asynchronous/test_max_staleness.py | 147 ++++++++++++++++++++++++ test/test_max_staleness.py | 11 +- tools/synchro.py | 1 + 3 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 test/asynchronous/test_max_staleness.py diff --git a/test/asynchronous/test_max_staleness.py b/test/asynchronous/test_max_staleness.py new file mode 100644 index 0000000000..eace7a2950 --- /dev/null +++ b/test/asynchronous/test_max_staleness.py @@ -0,0 +1,147 @@ +# Copyright 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test maxStalenessSeconds support.""" +from __future__ import annotations + +import os +import sys +import time +import warnings +from pathlib import Path + +from pymongo import AsyncMongoClient +from pymongo.operations import _Op + +sys.path[0:0] = [""] + +from test.asynchronous import AsyncPyMongoTestCase, async_client_context, unittest +from test.utils_selection_tests import create_selection_tests + +from pymongo.errors import ConfigurationError +from pymongo.server_selectors import writable_server_selector + +_IS_SYNC = False + +# Location of JSON test specifications. +if _IS_SYNC: + TEST_PATH = os.path.join(Path(__file__).resolve().parent, "max_staleness") +else: + TEST_PATH = os.path.join(Path(__file__).resolve().parent.parent, "max_staleness") + +class TestAllScenarios(create_selection_tests(TEST_PATH)): # type: ignore + pass + + +class TestMaxStaleness(AsyncPyMongoTestCase): + async def test_max_staleness(self): + client = self.simple_client() + self.assertEqual(-1, client.read_preference.max_staleness) + + client = self.simple_client("mongodb://a/?readPreference=secondary") + self.assertEqual(-1, client.read_preference.max_staleness) + + # These tests are specified in max-staleness-tests.rst. + with self.assertRaises(ConfigurationError): + # Default read pref "primary" can't be used with max staleness. + self.simple_client("mongodb://a/?maxStalenessSeconds=120") + + with self.assertRaises(ConfigurationError): + # Read pref "primary" can't be used with max staleness. + self.simple_client("mongodb://a/?readPreference=primary&maxStalenessSeconds=120") + + client = self.simple_client("mongodb://host/?maxStalenessSeconds=-1") + self.assertEqual(-1, client.read_preference.max_staleness) + + client = self.simple_client("mongodb://host/?readPreference=primary&maxStalenessSeconds=-1") + self.assertEqual(-1, client.read_preference.max_staleness) + + client = self.simple_client( + "mongodb://host/?readPreference=secondary&maxStalenessSeconds=120" + ) + self.assertEqual(120, client.read_preference.max_staleness) + + client = self.simple_client("mongodb://a/?readPreference=secondary&maxStalenessSeconds=1") + self.assertEqual(1, client.read_preference.max_staleness) + + client = self.simple_client("mongodb://a/?readPreference=secondary&maxStalenessSeconds=-1") + self.assertEqual(-1, client.read_preference.max_staleness) + + client = self.simple_client(maxStalenessSeconds=-1, readPreference="nearest") + self.assertEqual(-1, client.read_preference.max_staleness) + + with self.assertRaises(TypeError): + # Prohibit None. + self.simple_client(maxStalenessSeconds=None, readPreference="nearest") + + async def test_max_staleness_float(self): + with self.assertRaises(TypeError) as ctx: + await self.async_rs_or_single_client(maxStalenessSeconds=1.5, readPreference="nearest") + + self.assertIn("must be an integer", str(ctx.exception)) + + with warnings.catch_warnings(record=True) as ctx: + warnings.simplefilter("always") + client = self.simple_client( + "mongodb://host/?maxStalenessSeconds=1.5&readPreference=nearest" + ) + + # Option was ignored. + self.assertEqual(-1, client.read_preference.max_staleness) + self.assertIn("must be an integer", str(ctx[0])) + + async def test_max_staleness_zero(self): + # Zero is too small. + with self.assertRaises(ValueError) as ctx: + await self.async_rs_or_single_client(maxStalenessSeconds=0, readPreference="nearest") + + self.assertIn("must be a positive integer", str(ctx.exception)) + + with warnings.catch_warnings(record=True) as ctx: + warnings.simplefilter("always") + client = self.simple_client( + "mongodb://host/?maxStalenessSeconds=0&readPreference=nearest" + ) + + # Option was ignored. + self.assertEqual(-1, client.read_preference.max_staleness) + self.assertIn("must be a positive integer", str(ctx[0])) + + @async_client_context.require_replica_set + async def test_last_write_date(self): + # From max-staleness-tests.rst, "Parse lastWriteDate". + client = await self.async_rs_or_single_client(heartbeatFrequencyMS=500) + await client.pymongo_test.test.insert_one({}) + # Wait for the server description to be updated. + time.sleep(1) + server = await client._topology.select_server(writable_server_selector, _Op.TEST) + first = server.description.last_write_date + self.assertTrue(first) + # The first last_write_date may correspond to a internal server write, + # sleep so that the next write does not occur within the same second. + time.sleep(1) + await client.pymongo_test.test.insert_one({}) + # Wait for the server description to be updated. + time.sleep(1) + server = await client._topology.select_server(writable_server_selector, _Op.TEST) + second = server.description.last_write_date + assert first is not None + + assert second is not None + self.assertGreater(second, first) + self.assertLess(second, first + 10) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/test_max_staleness.py b/test/test_max_staleness.py index 32d09ada9a..c7e730595b 100644 --- a/test/test_max_staleness.py +++ b/test/test_max_staleness.py @@ -19,6 +19,7 @@ import sys import time import warnings +from pathlib import Path from pymongo import MongoClient from pymongo.operations import _Op @@ -31,11 +32,15 @@ from pymongo.errors import ConfigurationError from pymongo.server_selectors import writable_server_selector -# Location of JSON test specifications. -_TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "max_staleness") +_IS_SYNC = True +# Location of JSON test specifications. +if _IS_SYNC: + TEST_PATH = os.path.join(Path(__file__).resolve().parent, "max_staleness") +else: + TEST_PATH = os.path.join(Path(__file__).resolve().parent.parent, "max_staleness") -class TestAllScenarios(create_selection_tests(_TEST_PATH)): # type: ignore +class TestAllScenarios(create_selection_tests(TEST_PATH)): # type: ignore pass diff --git a/tools/synchro.py b/tools/synchro.py index dc272929ad..bfd578ec30 100644 --- a/tools/synchro.py +++ b/tools/synchro.py @@ -215,6 +215,7 @@ def async_only_test(f: str) -> bool: "test_gridfs_spec.py", "test_logger.py", "test_monitoring.py", + "test_max_staleness.py", "test_raw_bson.py", "test_retryable_reads.py", "test_retryable_writes.py", From b84438efc4e58c48d8bed30c4be570af1ca00e00 Mon Sep 17 00:00:00 2001 From: Iris Ho Date: Fri, 31 Jan 2025 08:57:40 -0800 Subject: [PATCH 2/3] fix test --- test/asynchronous/test_max_staleness.py | 9 ++++++--- test/test_max_staleness.py | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/test/asynchronous/test_max_staleness.py b/test/asynchronous/test_max_staleness.py index eace7a2950..4d2d04dbfb 100644 --- a/test/asynchronous/test_max_staleness.py +++ b/test/asynchronous/test_max_staleness.py @@ -15,6 +15,7 @@ """Test maxStalenessSeconds support.""" from __future__ import annotations +import asyncio import os import sys import time @@ -40,6 +41,7 @@ else: TEST_PATH = os.path.join(Path(__file__).resolve().parent.parent, "max_staleness") + class TestAllScenarios(create_selection_tests(TEST_PATH)): # type: ignore pass @@ -124,21 +126,22 @@ async def test_last_write_date(self): client = await self.async_rs_or_single_client(heartbeatFrequencyMS=500) await client.pymongo_test.test.insert_one({}) # Wait for the server description to be updated. - time.sleep(1) + await asyncio.sleep(1) server = await client._topology.select_server(writable_server_selector, _Op.TEST) first = server.description.last_write_date self.assertTrue(first) # The first last_write_date may correspond to a internal server write, # sleep so that the next write does not occur within the same second. - time.sleep(1) + await asyncio.sleep(1) await client.pymongo_test.test.insert_one({}) # Wait for the server description to be updated. - time.sleep(1) + await asyncio.sleep(1) server = await client._topology.select_server(writable_server_selector, _Op.TEST) second = server.description.last_write_date assert first is not None assert second is not None + print(second, first) self.assertGreater(second, first) self.assertLess(second, first + 10) diff --git a/test/test_max_staleness.py b/test/test_max_staleness.py index c7e730595b..afa129a2b3 100644 --- a/test/test_max_staleness.py +++ b/test/test_max_staleness.py @@ -15,6 +15,7 @@ """Test maxStalenessSeconds support.""" from __future__ import annotations +import asyncio import os import sys import time @@ -40,6 +41,7 @@ else: TEST_PATH = os.path.join(Path(__file__).resolve().parent.parent, "max_staleness") + class TestAllScenarios(create_selection_tests(TEST_PATH)): # type: ignore pass @@ -139,6 +141,7 @@ def test_last_write_date(self): assert first is not None assert second is not None + print(second, first) self.assertGreater(second, first) self.assertLess(second, first + 10) From 64da71195a5a55a9380ef18e309853132a451684 Mon Sep 17 00:00:00 2001 From: Iris Ho Date: Fri, 31 Jan 2025 09:57:16 -0800 Subject: [PATCH 3/3] remove print statement --- test/asynchronous/test_max_staleness.py | 1 - test/test_max_staleness.py | 1 - 2 files changed, 2 deletions(-) diff --git a/test/asynchronous/test_max_staleness.py b/test/asynchronous/test_max_staleness.py index 4d2d04dbfb..7dbf17021f 100644 --- a/test/asynchronous/test_max_staleness.py +++ b/test/asynchronous/test_max_staleness.py @@ -141,7 +141,6 @@ async def test_last_write_date(self): assert first is not None assert second is not None - print(second, first) self.assertGreater(second, first) self.assertLess(second, first + 10) diff --git a/test/test_max_staleness.py b/test/test_max_staleness.py index afa129a2b3..56e047fd4b 100644 --- a/test/test_max_staleness.py +++ b/test/test_max_staleness.py @@ -141,7 +141,6 @@ def test_last_write_date(self): assert first is not None assert second is not None - print(second, first) self.assertGreater(second, first) self.assertLess(second, first + 10)