-
Notifications
You must be signed in to change notification settings - Fork 1.1k
PYTHON-5075 Convert test.test_csot to async #2088
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 15 commits
0423d60
b021ede
469013b
c36586b
e256f4c
560b644
86dace2
27d6371
b5a9a0a
8d21d59
24ba493
71e1d63
7b2a920
dfb28e5
efad63b
ab5361f
a663f5f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
# Copyright 2022-present 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 the CSOT unified spec tests.""" | ||
from __future__ import annotations | ||
|
||
import os | ||
import sys | ||
from pathlib import Path | ||
|
||
sys.path[0:0] = [""] | ||
|
||
from test.asynchronous import AsyncIntegrationTest, async_client_context, unittest | ||
from test.asynchronous.unified_format import generate_test_classes | ||
|
||
import pymongo | ||
from pymongo import _csot | ||
from pymongo.errors import PyMongoError | ||
|
||
_IS_SYNC = False | ||
|
||
# Location of JSON test specifications. | ||
if _IS_SYNC: | ||
TEST_PATH = os.path.join(Path(__file__).resolve().parent, "csot") | ||
else: | ||
TEST_PATH = os.path.join(Path(__file__).resolve().parent.parent, "csot") | ||
|
||
# Generate unified tests. | ||
globals().update(generate_test_classes(TEST_PATH, module=__name__)) | ||
|
||
|
||
class TestCSOT(AsyncIntegrationTest): | ||
RUN_ON_SERVERLESS = True | ||
RUN_ON_LOAD_BALANCER = True | ||
|
||
async def test_timeout_nested(self): | ||
if os.environ.get("SKIP_CSOT_TESTS", ""): | ||
raise unittest.SkipTest("SKIP_CSOT_TESTS is set, skipping...") | ||
coll = self.db.coll | ||
self.assertEqual(_csot.get_timeout(), None) | ||
self.assertEqual(_csot.get_deadline(), float("inf")) | ||
self.assertEqual(_csot.get_rtt(), 0.0) | ||
with pymongo.timeout(10): | ||
await coll.find_one() | ||
self.assertEqual(_csot.get_timeout(), 10) | ||
deadline_10 = _csot.get_deadline() | ||
|
||
# Capped at the original 10 deadline. | ||
with pymongo.timeout(15): | ||
await coll.find_one() | ||
self.assertEqual(_csot.get_timeout(), 15) | ||
self.assertEqual(_csot.get_deadline(), deadline_10) | ||
|
||
# Should be reset to previous values | ||
self.assertEqual(_csot.get_timeout(), 10) | ||
self.assertEqual(_csot.get_deadline(), deadline_10) | ||
await coll.find_one() | ||
|
||
with pymongo.timeout(5): | ||
await coll.find_one() | ||
self.assertEqual(_csot.get_timeout(), 5) | ||
self.assertLess(_csot.get_deadline(), deadline_10) | ||
|
||
# Should be reset to previous values | ||
self.assertEqual(_csot.get_timeout(), 10) | ||
self.assertEqual(_csot.get_deadline(), deadline_10) | ||
await coll.find_one() | ||
|
||
# Should be reset to previous values | ||
self.assertEqual(_csot.get_timeout(), None) | ||
self.assertEqual(_csot.get_deadline(), float("inf")) | ||
self.assertEqual(_csot.get_rtt(), 0.0) | ||
|
||
@async_client_context.require_change_streams | ||
async def test_change_stream_can_resume_after_timeouts(self): | ||
if os.environ.get("SKIP_CSOT_TESTS", ""): | ||
raise unittest.SkipTest("SKIP_CSOT_TESTS is set, skipping...") | ||
coll = self.db.test | ||
await coll.insert_one({}) | ||
async with await coll.watch() as stream: | ||
with pymongo.timeout(0.1): | ||
with self.assertRaises(PyMongoError) as ctx: | ||
await stream.next() | ||
self.assertTrue(ctx.exception.timeout) | ||
self.assertTrue(stream.alive) | ||
with self.assertRaises(PyMongoError) as ctx: | ||
await stream.try_next() | ||
self.assertTrue(ctx.exception.timeout) | ||
self.assertTrue(stream.alive) | ||
# Resume before the insert on 3.6 because 4.0 is required to avoid skipping documents | ||
if async_client_context.version < (4, 0): | ||
await stream.try_next() | ||
await coll.insert_one({}) | ||
with pymongo.timeout(10): | ||
self.assertTrue(await stream.next()) | ||
self.assertTrue(stream.alive) | ||
# Timeout applies to entire next() call, not only individual commands. | ||
with pymongo.timeout(0.5): | ||
with self.assertRaises(PyMongoError) as ctx: | ||
await stream.next() | ||
self.assertTrue(ctx.exception.timeout) | ||
self.assertTrue(stream.alive) | ||
self.assertFalse(stream.alive) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1387,15 +1387,20 @@ async def run_scenario(self, spec, uri=None): | |
# transaction (from a test failure) from blocking collection/database | ||
# operations during test set up and tear down. | ||
await self.kill_all_sessions() | ||
self.addAsyncCleanup(self.kill_all_sessions) | ||
|
||
if "csot" in self.id().lower(): | ||
# Retry CSOT tests up to 2 times to deal with flakey tests. | ||
attempts = 3 | ||
for i in range(attempts): | ||
try: | ||
return await self._run_scenario(spec, uri) | ||
except AssertionError: | ||
except (AssertionError, OperationFailure) as exc: | ||
if not ( | ||
isinstance(exc, OperationFailure) | ||
and not _IS_SYNC | ||
and "failpoint" in exc._message | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Try something like this instead:
That way it's clear that we raise any OperationFailures that are either thrown by a sync test or don't have the failpoint message. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah yeah, that's cleaner |
||
): | ||
raise | ||
if i < attempts - 1: | ||
print( | ||
f"Retrying after attempt {i+1} of {self.id()} failed with:\n" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to do this for all CSOT tests, or only the asynchronous ones?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OHHH good point, probably just the async ones right? I don't think I've seen flakey sync tests failing with OperationFailure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, no need to change the synchronous behavior if it's not seeing the same issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if it was the best way to only retry in async, but i just changed the logic of the if statement inside the except