From d79eee51babeab3effa901da36daaf06a5241bd0 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 30 Jul 2024 15:09:45 -0700 Subject: [PATCH] PYTHON-4521 Example transition of an existing test to an async one (#1702) --- test/asynchronous/test_logger.py | 103 +++++++++++++++++++++++++++++++ test/test_logger.py | 7 ++- tools/synchro.py | 1 + 3 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 test/asynchronous/test_logger.py diff --git a/test/asynchronous/test_logger.py b/test/asynchronous/test_logger.py new file mode 100644 index 000000000..7a5884651 --- /dev/null +++ b/test/asynchronous/test_logger.py @@ -0,0 +1,103 @@ +# Copyright 2023-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. +from __future__ import annotations + +import os +from test import unittest +from test.asynchronous import AsyncIntegrationTest +from test.utils import async_single_client +from unittest.mock import patch + +from bson import json_util +from pymongo.errors import OperationFailure +from pymongo.logger import _DEFAULT_DOCUMENT_LENGTH + +_IS_SYNC = False + + +# https://github.com/mongodb/specifications/tree/master/source/command-logging-and-monitoring/tests#prose-tests +class TestLogger(AsyncIntegrationTest): + async def test_default_truncation_limit(self): + docs = [{"x": "y"} for _ in range(100)] + db = self.db + + with patch.dict("os.environ"): + os.environ.pop("MONGOB_LOG_MAX_DOCUMENT_LENGTH", None) + with self.assertLogs("pymongo.command", level="DEBUG") as cm: + await db.test.insert_many(docs) + + cmd_started_log = json_util.loads(cm.records[0].message) + self.assertEqual(len(cmd_started_log["command"]), _DEFAULT_DOCUMENT_LENGTH + 3) + + cmd_succeeded_log = json_util.loads(cm.records[1].message) + self.assertLessEqual(len(cmd_succeeded_log["reply"]), _DEFAULT_DOCUMENT_LENGTH + 3) + + with self.assertLogs("pymongo.command", level="DEBUG") as cm: + await db.test.find({}).to_list() + cmd_succeeded_log = json_util.loads(cm.records[1].message) + self.assertEqual(len(cmd_succeeded_log["reply"]), _DEFAULT_DOCUMENT_LENGTH + 3) + + async def test_configured_truncation_limit(self): + cmd = {"hello": True} + db = self.db + with patch.dict("os.environ", {"MONGOB_LOG_MAX_DOCUMENT_LENGTH": "5"}): + with self.assertLogs("pymongo.command", level="DEBUG") as cm: + await db.command(cmd) + + cmd_started_log = json_util.loads(cm.records[0].message) + self.assertEqual(len(cmd_started_log["command"]), 5 + 3) + + cmd_succeeded_log = json_util.loads(cm.records[1].message) + self.assertLessEqual(len(cmd_succeeded_log["reply"]), 5 + 3) + with self.assertRaises(OperationFailure): + await db.command({"notARealCommand": True}) + cmd_failed_log = json_util.loads(cm.records[-1].message) + self.assertEqual(len(cmd_failed_log["failure"]), 5 + 3) + + async def test_truncation_multi_byte_codepoints(self): + document_lengths = ["20000", "20001", "20002"] + multi_byte_char_str_len = 50_000 + str_to_repeat = "界" + + multi_byte_char_str = "" + for i in range(multi_byte_char_str_len): + multi_byte_char_str += str_to_repeat + + for length in document_lengths: + with patch.dict("os.environ", {"MONGOB_LOG_MAX_DOCUMENT_LENGTH": length}): + with self.assertLogs("pymongo.command", level="DEBUG") as cm: + await self.db.test.insert_one({"x": multi_byte_char_str}) + cmd_started_log = json_util.loads(cm.records[0].message)["command"] + + cmd_started_log = cmd_started_log[:-3] + last_3_bytes = cmd_started_log.encode()[-3:].decode() + + self.assertEqual(last_3_bytes, str_to_repeat) + + async def test_logging_without_listeners(self): + c = await async_single_client() + self.assertEqual(len(c._event_listeners.event_listeners()), 0) + with self.assertLogs("pymongo.connection", level="DEBUG") as cm: + await c.db.test.insert_one({"x": "1"}) + self.assertGreater(len(cm.records), 0) + with self.assertLogs("pymongo.command", level="DEBUG") as cm: + await c.db.test.insert_one({"x": "1"}) + self.assertGreater(len(cm.records), 0) + with self.assertLogs("pymongo.serverSelection", level="DEBUG") as cm: + await c.db.test.insert_one({"x": "1"}) + self.assertGreater(len(cm.records), 0) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/test_logger.py b/test/test_logger.py index 1dfa0724e..d6c30b68a 100644 --- a/test/test_logger.py +++ b/test/test_logger.py @@ -14,8 +14,7 @@ from __future__ import annotations import os -from test import unittest -from test.test_client import IntegrationTest +from test import IntegrationTest, unittest from test.utils import single_client from unittest.mock import patch @@ -23,6 +22,8 @@ from bson import json_util from pymongo.errors import OperationFailure from pymongo.logger import _DEFAULT_DOCUMENT_LENGTH +_IS_SYNC = True + # https://github.com/mongodb/specifications/tree/master/source/command-logging-and-monitoring/tests#prose-tests class TestLogger(IntegrationTest): @@ -42,7 +43,7 @@ class TestLogger(IntegrationTest): self.assertLessEqual(len(cmd_succeeded_log["reply"]), _DEFAULT_DOCUMENT_LENGTH + 3) with self.assertLogs("pymongo.command", level="DEBUG") as cm: - list(db.test.find({})) + db.test.find({}).to_list() cmd_succeeded_log = json_util.loads(cm.records[1].message) self.assertEqual(len(cmd_succeeded_log["reply"]), _DEFAULT_DOCUMENT_LENGTH + 3) diff --git a/tools/synchro.py b/tools/synchro.py index 68fd2a159..94f3d7f8f 100644 --- a/tools/synchro.py +++ b/tools/synchro.py @@ -154,6 +154,7 @@ converted_tests = [ "test_collection.py", "test_cursor.py", "test_database.py", + "test_logger.py", "test_session.py", "test_transactions.py", ]