From 8f58db05b992af9f6ab833ccaa06015f9e10eaa8 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Mon, 22 Jul 2024 15:47:41 -0700 Subject: [PATCH 1/5] PYTHON-4565 - Use pytest markers for test suite configuration --- .evergreen/run-tests.sh | 29 ++++++++++--------- pyproject.toml | 17 +++++++++-- test/asynchronous/conftest.py | 6 ++++ test/atlas/test_connection.py | 5 ++++ test/auth_aws/test_auth_aws.py | 9 +++++- test/auth_oidc/test_auth_oidc.py | 5 ++++ test/conftest.py | 6 ++++ test/mockupdb/test_auth_recovering_member.py | 9 +++++- test/mockupdb/test_cluster_time.py | 9 +++++- test/mockupdb/test_cursor.py | 9 +++++- test/mockupdb/test_cursor_namespace.py | 9 +++++- test/mockupdb/test_getmore_sharded.py | 9 +++++- test/mockupdb/test_handshake.py | 9 +++++- test/mockupdb/test_initial_ismaster.py | 9 +++++- test/mockupdb/test_list_indexes.py | 9 +++++- test/mockupdb/test_max_staleness.py | 9 +++++- test/mockupdb/test_mixed_version_sharded.py | 9 +++++- .../mockupdb/test_mongos_command_read_mode.py | 9 +++++- .../test_network_disconnect_primary.py | 9 +++++- test/mockupdb/test_op_msg.py | 9 +++++- test/mockupdb/test_op_msg_read_preference.py | 9 +++++- test/mockupdb/test_query_read_pref_sharded.py | 9 +++++- test/mockupdb/test_reset_and_request_check.py | 10 ++++++- test/mockupdb/test_rsghost.py | 9 +++++- test/mockupdb/test_slave_okay_rs.py | 9 +++++- test/mockupdb/test_slave_okay_sharded.py | 9 +++++- test/mockupdb/test_slave_okay_single.py | 9 +++++- test/mockupdb/test_standalone_shard.py | 9 +++++- test/ocsp/test_ocsp.py | 5 ++++ test/performance/perf_test.py | 4 +++ test/test_data_lake.py | 5 ++++ test/test_encryption.py | 4 +++ test/test_index_management.py | 4 +++ test/test_load_balancer.py | 4 +++ test/test_on_demand_csfle.py | 4 +++ 35 files changed, 260 insertions(+), 37 deletions(-) diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index c93c3b82ac..4ada73141d 100755 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -30,7 +30,7 @@ set -o xtrace AUTH=${AUTH:-noauth} SSL=${SSL:-nossl} -TEST_SUITES="test/ test/asynchronous/" +TEST_SUITES="" TEST_ARGS="${*:1}" export PIP_QUIET=1 # Quiet by default @@ -96,7 +96,7 @@ if [ -n "$TEST_LOADBALANCER" ]; then export LOAD_BALANCER=1 export SINGLE_MONGOS_LB_URI="${SINGLE_MONGOS_LB_URI:-mongodb://127.0.0.1:8000/?loadBalanced=true}" export MULTI_MONGOS_LB_URI="${MULTI_MONGOS_LB_URI:-mongodb://127.0.0.1:8001/?loadBalanced=true}" - export TEST_SUITES="test/test_load_balancer.py" + export TEST_SUITES="load_balancer" fi if [ "$SSL" != "nossl" ]; then @@ -172,7 +172,7 @@ if [ -n "$TEST_ENCRYPTION" ]; then export PATH=$CRYPT_SHARED_DIR:$PATH fi # Only run the encryption tests. - TEST_SUITES="test/test_encryption.py" + TEST_SUITES="encryption" fi if [ -n "$TEST_FLE_AZURE_AUTO" ] || [ -n "$TEST_FLE_GCP_AUTO" ]; then @@ -185,8 +185,7 @@ if [ -n "$TEST_FLE_AZURE_AUTO" ] || [ -n "$TEST_FLE_GCP_AUTO" ]; then echo "MONGODB_URI unexpectedly contains user credentials in FLE test!"; exit 1 fi - - TEST_SUITES="test/test_on_demand_csfle.py" + TEST_SUITES="csfle" fi if [ -n "$TEST_INDEX_MANAGEMENT" ]; then @@ -195,36 +194,36 @@ if [ -n "$TEST_INDEX_MANAGEMENT" ]; then set +x export DB_PASSWORD="${DRIVERS_ATLAS_LAMBDA_PASSWORD}" set -x - TEST_SUITES="test/test_index_management.py" + TEST_SUITES="index_management" fi if [ -n "$TEST_DATA_LAKE" ] && [ -z "$TEST_ARGS" ]; then - TEST_SUITES="test/test_data_lake.py" + TEST_SUITES="data_lake" fi if [ -n "$TEST_ATLAS" ]; then - TEST_SUITES="test/atlas/test_connection.py" + TEST_SUITES="atlas" fi if [ -n "$TEST_OCSP" ]; then python -m pip install ".[ocsp]" - TEST_SUITES="test/ocsp/test_ocsp.py" + TEST_SUITES="ocsp" fi if [ -n "$TEST_AUTH_AWS" ]; then python -m pip install ".[aws]" - TEST_SUITES="test/auth_aws/test_auth_aws.py" + TEST_SUITES="auth_aws" fi if [ -n "$TEST_AUTH_OIDC" ]; then python -m pip install ".[aws]" - TEST_SUITES="test/auth_oidc/test_auth_oidc.py $TEST_ARGS" + TEST_SUITES="auth_oidc" fi if [ -n "$PERF_TEST" ]; then python -m pip install simplejson start_time=$(date +%s) - TEST_SUITES="test/performance/perf_test.py" + TEST_SUITES="perf" fi echo "Running $AUTH tests over $SSL with python $(which python)" @@ -254,7 +253,11 @@ PIP_QUIET=0 python -m pip list if [ -z "$GREEN_FRAMEWORK" ]; then # Use --capture=tee-sys so pytest prints test output inline: # https://docs.pytest.org/en/stable/how-to/capture-stdout-stderr.html - python -m pytest -v --capture=tee-sys --durations=5 --maxfail=10 $TEST_SUITES $TEST_ARGS + if [ -z "$TEST_SUITES" ]; then + python -m pytest -v --capture=tee-sys --durations=5 --maxfail=10 $TEST_ARGS + else + python -m pytest -v --capture=tee-sys --durations=5 --maxfail=10 -m $TEST_SUITES $TEST_ARGS + fi else python green_framework_test.py $GREEN_FRAMEWORK -v $TEST_ARGS fi diff --git a/pyproject.toml b/pyproject.toml index 09aa8028af..cfd994f563 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,10 +70,9 @@ zstd = ["requirements/zstd.txt"] [tool.pytest.ini_options] minversion = "7" -addopts = ["-ra", "--strict-config", "--strict-markers", "--junitxml=xunit-results/TEST-results.xml"] +addopts = ["-ra", "--strict-config", "--strict-markers", "--junitxml=xunit-results/TEST-results.xml", "-m default"] testpaths = ["test"] log_cli_level = "INFO" -norecursedirs = ["test/*"] faulthandler_timeout = 1500 xfail_strict = true filterwarnings = [ @@ -96,6 +95,20 @@ filterwarnings = [ # https://github.com/dateutil/dateutil/issues/1314 "module:datetime.datetime.utc:DeprecationWarning:dateutil", ] +markers = [ + "auth_aws: tests that rely on pymongo-auth-aws", + "auth_oidc: tests that rely on oidc auth", + "ocsp: tests that rely on ocsp", + "atlas: tests that rely on atlas", + "data_lake: tests that rely on atlas data lake", + "perf: benchmark tests", + "index_management: index management tests", + "csfle: client-side field-level encryption tests", + "encryption: encryption tests", + "load_balancer: load balancer tests", + "mockupdb: tests that rely on mockupdb", + "default: default test suite", +] [tool.mypy] strict = true diff --git a/test/asynchronous/conftest.py b/test/asynchronous/conftest.py index 28e3890d9c..398ba4265c 100644 --- a/test/asynchronous/conftest.py +++ b/test/asynchronous/conftest.py @@ -12,3 +12,9 @@ async def test_setup_and_teardown(): await async_setup() yield await async_teardown() + + +def pytest_collection_modifyitems(items, config): + for item in items: + if not any(item.iter_markers()): + item.add_marker("default") diff --git a/test/atlas/test_connection.py b/test/atlas/test_connection.py index 2c45241ea8..762ac30115 100644 --- a/test/atlas/test_connection.py +++ b/test/atlas/test_connection.py @@ -20,11 +20,16 @@ import unittest from collections import defaultdict +import pytest + sys.path[0:0] = [""] import pymongo from pymongo.ssl_support import HAS_SNI +pytestmark = pytest.mark.atlas + + URIS = { "ATLAS_REPL": os.environ.get("ATLAS_REPL"), "ATLAS_SHRD": os.environ.get("ATLAS_SHRD"), diff --git a/test/auth_aws/test_auth_aws.py b/test/auth_aws/test_auth_aws.py index 3e5dcec563..10416ae5fe 100644 --- a/test/auth_aws/test_auth_aws.py +++ b/test/auth_aws/test_auth_aws.py @@ -20,14 +20,21 @@ import unittest from unittest.mock import patch +import pytest + sys.path[0:0] = [""] -from pymongo_auth_aws import AwsCredential, auth +try: + from pymongo_auth_aws import AwsCredential, auth +except ImportError: + pass from pymongo import MongoClient from pymongo.errors import OperationFailure from pymongo.uri_parser import parse_uri +pytestmark = pytest.mark.auth_aws + class TestAuthAWS(unittest.TestCase): uri: str diff --git a/test/auth_oidc/test_auth_oidc.py b/test/auth_oidc/test_auth_oidc.py index 83e25e685a..406ea5ec1b 100644 --- a/test/auth_oidc/test_auth_oidc.py +++ b/test/auth_oidc/test_auth_oidc.py @@ -25,6 +25,8 @@ from pathlib import Path from typing import Dict +import pytest + sys.path[0:0] = [""] from test.unified_format import generate_test_classes @@ -55,6 +57,8 @@ # Generate unified tests. globals().update(generate_test_classes(str(TEST_PATH), module=__name__)) +pytestmark = pytest.mark.auth_oidc + class OIDCTestBase(unittest.TestCase): @classmethod @@ -96,6 +100,7 @@ def fail_point(self, command_args): client.admin.command("configureFailPoint", cmd_on["configureFailPoint"], mode="off") +@pytest.mark.auth_oidc class TestAuthOIDCHuman(OIDCTestBase): uri: str diff --git a/test/conftest.py b/test/conftest.py index 58f04ea7c3..39d29355b7 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -12,3 +12,9 @@ def test_setup_and_teardown(): setup() yield teardown() + + +def pytest_collection_modifyitems(items, config): + for item in items: + if not any(item.iter_markers()): + item.add_marker("default") diff --git a/test/mockupdb/test_auth_recovering_member.py b/test/mockupdb/test_auth_recovering_member.py index 2051a24af6..1ca490b036 100644 --- a/test/mockupdb/test_auth_recovering_member.py +++ b/test/mockupdb/test_auth_recovering_member.py @@ -15,11 +15,18 @@ import unittest -from mockupdb import MockupDB +import pytest + +try: + from mockupdb import MockupDB +except ImportError: + pass from pymongo import MongoClient from pymongo.errors import ServerSelectionTimeoutError +pytestmark = pytest.mark.mockupdb + class TestAuthRecoveringMember(unittest.TestCase): def test_auth_recovering_member(self): diff --git a/test/mockupdb/test_cluster_time.py b/test/mockupdb/test_cluster_time.py index a64804541b..c0bc601d0c 100644 --- a/test/mockupdb/test_cluster_time.py +++ b/test/mockupdb/test_cluster_time.py @@ -17,11 +17,18 @@ import unittest -from mockupdb import MockupDB, going +import pytest + +try: + from mockupdb import MockupDB, going +except ImportError: + pass from bson import Timestamp from pymongo import DeleteMany, InsertOne, MongoClient, UpdateOne +pytestmark = pytest.mark.mockupdb + class TestClusterTime(unittest.TestCase): def cluster_time_conversation(self, callback, replies): diff --git a/test/mockupdb/test_cursor.py b/test/mockupdb/test_cursor.py index 96a7e17053..1284d965af 100644 --- a/test/mockupdb/test_cursor.py +++ b/test/mockupdb/test_cursor.py @@ -18,12 +18,19 @@ import unittest from test import PyMongoTestCase -from mockupdb import MockupDB, OpMsg, going +import pytest + +try: + from mockupdb import MockupDB, OpMsg, going +except ImportError: + pass from bson.objectid import ObjectId from pymongo import MongoClient from pymongo.errors import OperationFailure +pytestmark = pytest.mark.mockupdb + class TestCursor(unittest.TestCase): def test_getmore_load_balanced(self): diff --git a/test/mockupdb/test_cursor_namespace.py b/test/mockupdb/test_cursor_namespace.py index e6713abf10..d32abc3f43 100644 --- a/test/mockupdb/test_cursor_namespace.py +++ b/test/mockupdb/test_cursor_namespace.py @@ -17,10 +17,17 @@ import unittest -from mockupdb import MockupDB, going +import pytest + +try: + from mockupdb import MockupDB, going +except ImportError: + pass from pymongo import MongoClient +pytestmark = pytest.mark.mockupdb + class TestCursorNamespace(unittest.TestCase): server: MockupDB diff --git a/test/mockupdb/test_getmore_sharded.py b/test/mockupdb/test_getmore_sharded.py index b06b48f01c..c354a69ae7 100644 --- a/test/mockupdb/test_getmore_sharded.py +++ b/test/mockupdb/test_getmore_sharded.py @@ -18,10 +18,17 @@ import unittest from queue import Queue -from mockupdb import MockupDB, going +import pytest + +try: + from mockupdb import MockupDB, going +except ImportError: + pass from pymongo import MongoClient +pytestmark = pytest.mark.mockupdb + class TestGetmoreSharded(unittest.TestCase): def test_getmore_sharded(self): diff --git a/test/mockupdb/test_handshake.py b/test/mockupdb/test_handshake.py index 00cae32ee5..ed621f0f35 100644 --- a/test/mockupdb/test_handshake.py +++ b/test/mockupdb/test_handshake.py @@ -15,7 +15,12 @@ import unittest -from mockupdb import Command, MockupDB, OpMsg, OpMsgReply, OpQuery, OpReply, absent, go +import pytest + +try: + from mockupdb import Command, MockupDB, OpMsg, OpMsgReply, OpQuery, OpReply, absent, go +except ImportError: + pass from bson.objectid import ObjectId from pymongo import MongoClient @@ -23,6 +28,8 @@ from pymongo.errors import OperationFailure from pymongo.server_api import ServerApi, ServerApiVersion +pytestmark = pytest.mark.mockupdb + def _check_handshake_data(request): assert "client" in request diff --git a/test/mockupdb/test_initial_ismaster.py b/test/mockupdb/test_initial_ismaster.py index 97864dd257..1caf65c3a8 100644 --- a/test/mockupdb/test_initial_ismaster.py +++ b/test/mockupdb/test_initial_ismaster.py @@ -16,10 +16,17 @@ import time import unittest -from mockupdb import MockupDB, wait_until +import pytest + +try: + from mockupdb import MockupDB, wait_until +except ImportError: + pass from pymongo import MongoClient +pytestmark = pytest.mark.mockupdb + class TestInitialIsMaster(unittest.TestCase): def test_initial_ismaster(self): diff --git a/test/mockupdb/test_list_indexes.py b/test/mockupdb/test_list_indexes.py index 163c25c37b..203b0cc633 100644 --- a/test/mockupdb/test_list_indexes.py +++ b/test/mockupdb/test_list_indexes.py @@ -17,11 +17,18 @@ import unittest -from mockupdb import MockupDB, going +import pytest + +try: + from mockupdb import MockupDB, going +except ImportError: + pass from bson import SON from pymongo import MongoClient +pytestmark = pytest.mark.mockupdb + class TestListIndexes(unittest.TestCase): def test_list_indexes_command(self): diff --git a/test/mockupdb/test_max_staleness.py b/test/mockupdb/test_max_staleness.py index 88a3c13e63..4ec5a037ae 100644 --- a/test/mockupdb/test_max_staleness.py +++ b/test/mockupdb/test_max_staleness.py @@ -15,10 +15,17 @@ import unittest -from mockupdb import MockupDB, going +import pytest + +try: + from mockupdb import MockupDB, going +except ImportError: + pass from pymongo import MongoClient +pytestmark = pytest.mark.mockupdb + class TestMaxStalenessMongos(unittest.TestCase): def test_mongos(self): diff --git a/test/mockupdb/test_mixed_version_sharded.py b/test/mockupdb/test_mixed_version_sharded.py index 515c279879..a656fc6926 100644 --- a/test/mockupdb/test_mixed_version_sharded.py +++ b/test/mockupdb/test_mixed_version_sharded.py @@ -19,11 +19,18 @@ import unittest from queue import Queue -from mockupdb import MockupDB, go +import pytest + +try: + from mockupdb import MockupDB, go +except ImportError: + pass from operations import upgrades # type: ignore[import] from pymongo import MongoClient +pytestmark = pytest.mark.mockupdb + class TestMixedVersionSharded(unittest.TestCase): def setup_server(self, upgrade): diff --git a/test/mockupdb/test_mongos_command_read_mode.py b/test/mockupdb/test_mongos_command_read_mode.py index 8ee33431a8..952aa1130a 100644 --- a/test/mockupdb/test_mongos_command_read_mode.py +++ b/test/mockupdb/test_mongos_command_read_mode.py @@ -16,7 +16,12 @@ import itertools import unittest -from mockupdb import MockupDB, OpMsg, going +import pytest + +try: + from mockupdb import MockupDB, OpMsg, going +except ImportError: + pass from operations import operations # type: ignore[import] from pymongo import MongoClient, ReadPreference @@ -26,6 +31,8 @@ read_pref_mode_from_name, ) +pytestmark = pytest.mark.mockupdb + class TestMongosCommandReadMode(unittest.TestCase): def test_aggregate(self): diff --git a/test/mockupdb/test_network_disconnect_primary.py b/test/mockupdb/test_network_disconnect_primary.py index d05cfb531a..d36149aee5 100644 --- a/test/mockupdb/test_network_disconnect_primary.py +++ b/test/mockupdb/test_network_disconnect_primary.py @@ -15,12 +15,19 @@ import unittest -from mockupdb import Future, MockupDB, OpReply, going, wait_until +import pytest + +try: + from mockupdb import Future, MockupDB, OpReply, going, wait_until +except ImportError: + pass from pymongo import MongoClient from pymongo.errors import ConnectionFailure from pymongo.topology_description import TOPOLOGY_TYPE +pytestmark = pytest.mark.mockupdb + class TestNetworkDisconnectPrimary(unittest.TestCase): def test_network_disconnect_primary(self): diff --git a/test/mockupdb/test_op_msg.py b/test/mockupdb/test_op_msg.py index d36e5e02b6..0bd5885c6b 100644 --- a/test/mockupdb/test_op_msg.py +++ b/test/mockupdb/test_op_msg.py @@ -16,12 +16,19 @@ import unittest from collections import namedtuple -from mockupdb import OP_MSG_FLAGS, MockupDB, OpMsg, OpMsgReply, going +import pytest + +try: + from mockupdb import OP_MSG_FLAGS, MockupDB, OpMsg, OpMsgReply, going +except ImportError: + pass from pymongo import MongoClient, WriteConcern from pymongo.cursor_shared import CursorType from pymongo.operations import DeleteOne, InsertOne, UpdateOne +pytestmark = pytest.mark.mockupdb + Operation = namedtuple("Operation", ["name", "function", "request", "reply"]) operations = [ diff --git a/test/mockupdb/test_op_msg_read_preference.py b/test/mockupdb/test_op_msg_read_preference.py index 0fa7b84861..ebb1b08143 100644 --- a/test/mockupdb/test_op_msg_read_preference.py +++ b/test/mockupdb/test_op_msg_read_preference.py @@ -18,7 +18,12 @@ import unittest from typing import Any -from mockupdb import CommandBase, MockupDB, going +import pytest + +try: + from mockupdb import CommandBase, MockupDB, going +except ImportError: + pass from operations import operations # type: ignore[import] from pymongo import MongoClient, ReadPreference @@ -28,6 +33,8 @@ read_pref_mode_from_name, ) +pytestmark = pytest.mark.mockupdb + class OpMsgReadPrefBase(unittest.TestCase): single_mongod = False diff --git a/test/mockupdb/test_query_read_pref_sharded.py b/test/mockupdb/test_query_read_pref_sharded.py index 5297709886..62959f651d 100644 --- a/test/mockupdb/test_query_read_pref_sharded.py +++ b/test/mockupdb/test_query_read_pref_sharded.py @@ -17,7 +17,12 @@ import unittest -from mockupdb import MockupDB, OpMsg, going +import pytest + +try: + from mockupdb import MockupDB, OpMsg, going +except ImportError: + pass from bson import SON from pymongo import MongoClient @@ -29,6 +34,8 @@ SecondaryPreferred, ) +pytestmark = pytest.mark.mockupdb + class TestQueryAndReadModeSharded(unittest.TestCase): def test_query_and_read_mode_sharded_op_msg(self): diff --git a/test/mockupdb/test_reset_and_request_check.py b/test/mockupdb/test_reset_and_request_check.py index 19dfb9e395..46080a4604 100644 --- a/test/mockupdb/test_reset_and_request_check.py +++ b/test/mockupdb/test_reset_and_request_check.py @@ -17,7 +17,13 @@ import time import unittest -from mockupdb import MockupDB, going, wait_until +import pytest + +try: + from mockupdb import MockupDB, going, wait_until +except ImportError: + pass + from operations import operations # type: ignore[import] from pymongo import MongoClient @@ -25,6 +31,8 @@ from pymongo.operations import _Op from pymongo.server_type import SERVER_TYPE +pytestmark = pytest.mark.mockupdb + class TestResetAndRequestCheck(unittest.TestCase): def __init__(self, *args, **kwargs): diff --git a/test/mockupdb/test_rsghost.py b/test/mockupdb/test_rsghost.py index c9f2b89f07..c678207f61 100644 --- a/test/mockupdb/test_rsghost.py +++ b/test/mockupdb/test_rsghost.py @@ -18,11 +18,18 @@ import datetime import unittest -from mockupdb import MockupDB, going +import pytest + +try: + from mockupdb import MockupDB, going +except ImportError: + pass from pymongo import MongoClient from pymongo.errors import ServerSelectionTimeoutError +pytestmark = pytest.mark.mockupdb + class TestRSGhost(unittest.TestCase): def test_rsghost(self): diff --git a/test/mockupdb/test_slave_okay_rs.py b/test/mockupdb/test_slave_okay_rs.py index ba5b976d6b..94e798f8d8 100644 --- a/test/mockupdb/test_slave_okay_rs.py +++ b/test/mockupdb/test_slave_okay_rs.py @@ -20,11 +20,18 @@ import unittest -from mockupdb import MockupDB, going +import pytest + +try: + from mockupdb import MockupDB, going +except ImportError: + pass from operations import operations # type: ignore[import] from pymongo import MongoClient +pytestmark = pytest.mark.mockupdb + class TestSlaveOkayRS(unittest.TestCase): def setup_server(self): diff --git a/test/mockupdb/test_slave_okay_sharded.py b/test/mockupdb/test_slave_okay_sharded.py index 45b7d51ba0..398155ec5c 100644 --- a/test/mockupdb/test_slave_okay_sharded.py +++ b/test/mockupdb/test_slave_okay_sharded.py @@ -24,12 +24,19 @@ import unittest from queue import Queue -from mockupdb import MockupDB, going +import pytest + +try: + from mockupdb import MockupDB, going +except ImportError: + pass from operations import operations # type: ignore[import] from pymongo import MongoClient from pymongo.read_preferences import make_read_preference, read_pref_mode_from_name +pytestmark = pytest.mark.mockupdb + class TestSlaveOkaySharded(unittest.TestCase): def setup_server(self): diff --git a/test/mockupdb/test_slave_okay_single.py b/test/mockupdb/test_slave_okay_single.py index b03232807e..a7b2e6007d 100644 --- a/test/mockupdb/test_slave_okay_single.py +++ b/test/mockupdb/test_slave_okay_single.py @@ -23,13 +23,20 @@ import itertools import unittest -from mockupdb import MockupDB, going +import pytest + +try: + from mockupdb import MockupDB, going +except ImportError: + pass from operations import operations # type: ignore[import] from pymongo import MongoClient from pymongo.read_preferences import make_read_preference, read_pref_mode_from_name from pymongo.topology_description import TOPOLOGY_TYPE +pytestmark = pytest.mark.mockupdb + def topology_type_name(client): topology_type = client._topology._description.topology_type diff --git a/test/mockupdb/test_standalone_shard.py b/test/mockupdb/test_standalone_shard.py index 8d388cf74f..0c9dc07ecd 100644 --- a/test/mockupdb/test_standalone_shard.py +++ b/test/mockupdb/test_standalone_shard.py @@ -17,11 +17,18 @@ import unittest -from mockupdb import MockupDB, going +import pytest + +try: + from mockupdb import MockupDB, going +except ImportError: + pass from pymongo import MongoClient from pymongo.errors import OperationFailure +pytestmark = pytest.mark.mockupdb + class TestStandaloneShard(unittest.TestCase): # See PYTHON-2048 and SERVER-44591. diff --git a/test/ocsp/test_ocsp.py b/test/ocsp/test_ocsp.py index b4f15dc14d..fe7f21160e 100644 --- a/test/ocsp/test_ocsp.py +++ b/test/ocsp/test_ocsp.py @@ -20,11 +20,16 @@ import sys import unittest +import pytest + sys.path[0:0] = [""] import pymongo from pymongo.errors import ServerSelectionTimeoutError +pytestmark = pytest.mark.ocsp + + CA_FILE = os.environ.get("CA_FILE") OCSP_TLS_SHOULD_SUCCEED = os.environ.get("OCSP_TLS_SHOULD_SUCCEED") == "true" diff --git a/test/performance/perf_test.py b/test/performance/perf_test.py index 43b4da786a..a0aad36289 100644 --- a/test/performance/perf_test.py +++ b/test/performance/perf_test.py @@ -48,6 +48,8 @@ import warnings from typing import Any, List, Optional +import pytest + try: import simplejson as json except ImportError: @@ -61,6 +63,8 @@ from gridfs import GridFSBucket from pymongo import MongoClient +pytestmark = pytest.mark.perf + # Spec says to use at least 1 minute cumulative execution time and up to 100 iterations or 5 minutes but that # makes the benchmarks too slow. Instead, we use at least 30 seconds and at most 60 seconds. NUM_ITERATIONS = 100 diff --git a/test/test_data_lake.py b/test/test_data_lake.py index 283ef074cb..a11bd9b9cd 100644 --- a/test/test_data_lake.py +++ b/test/test_data_lake.py @@ -18,6 +18,8 @@ import os import sys +import pytest + sys.path[0:0] = [""] from test import IntegrationTest, client_context, unittest @@ -29,6 +31,9 @@ rs_or_single_client, ) +pytestmark = pytest.mark.data_lake + + # Location of JSON test specifications. _TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data_lake") diff --git a/test/test_encryption.py b/test/test_encryption.py index 0423f748ca..3c8d066a20 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -30,6 +30,8 @@ from threading import Thread from typing import Any, Dict, Mapping +import pytest + from pymongo.daemon import _spawn_daemon from pymongo.synchronous.collection import Collection @@ -91,6 +93,8 @@ from pymongo.synchronous.mongo_client import MongoClient from pymongo.write_concern import WriteConcern +pytestmark = pytest.mark.encryption + KMS_PROVIDERS = {"local": {"key": b"\x00" * 96}} diff --git a/test/test_index_management.py b/test/test_index_management.py index 5b6653dcba..426d0c7349 100644 --- a/test/test_index_management.py +++ b/test/test_index_management.py @@ -21,6 +21,8 @@ import uuid from typing import Any, Mapping +import pytest + sys.path[0:0] = [""] from test import IntegrationTest, unittest @@ -33,6 +35,8 @@ from pymongo.read_concern import ReadConcern from pymongo.write_concern import WriteConcern +pytestmark = pytest.mark.index_management + _TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "index_management") _NAME = "test-search-index" diff --git a/test/test_load_balancer.py b/test/test_load_balancer.py index 0fb877652f..a4db7395f1 100644 --- a/test/test_load_balancer.py +++ b/test/test_load_balancer.py @@ -20,12 +20,16 @@ import sys import threading +import pytest + sys.path[0:0] = [""] from test import IntegrationTest, client_context, unittest from test.unified_format import generate_test_classes from test.utils import ExceptionCatchingThread, get_pool, rs_client, wait_until +pytestmark = pytest.mark.load_balancer + # Location of JSON test specifications. TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "load_balancer") diff --git a/test/test_on_demand_csfle.py b/test/test_on_demand_csfle.py index 73484772e5..023feca8c2 100644 --- a/test/test_on_demand_csfle.py +++ b/test/test_on_demand_csfle.py @@ -19,6 +19,8 @@ import sys import unittest +import pytest + sys.path[0:0] = [""] from test import IntegrationTest, client_context @@ -26,6 +28,8 @@ from bson.codec_options import CodecOptions from pymongo.synchronous.encryption import _HAVE_PYMONGOCRYPT, ClientEncryption, EncryptionError +pytestmark = pytest.mark.csfle + class TestonDemandGCPCredentials(IntegrationTest): @classmethod From afae2c69f8c239d850a7920105123d9747f7ec7c Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Mon, 22 Jul 2024 15:49:40 -0700 Subject: [PATCH 2/5] Optional mockupdb import --- test/mockupdb/operations.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/mockupdb/operations.py b/test/mockupdb/operations.py index 34302aa551..458d0228f3 100644 --- a/test/mockupdb/operations.py +++ b/test/mockupdb/operations.py @@ -15,7 +15,10 @@ from collections import namedtuple -from mockupdb import OpMsgReply, OpReply +try: + from mockupdb import OpMsgReply, OpReply +except ImportError: + pass from pymongo import ReadPreference From 94abeb9d836d2b360e1089c3783dd89232a4fe0b Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Mon, 22 Jul 2024 16:02:04 -0700 Subject: [PATCH 3/5] Fix mockupdb imports --- test/mockupdb/operations.py | 128 ++--- test/mockupdb/test_auth_recovering_member.py | 4 +- test/mockupdb/test_cluster_time.py | 5 +- test/mockupdb/test_cursor.py | 5 +- test/mockupdb/test_cursor_namespace.py | 5 +- test/mockupdb/test_getmore_sharded.py | 5 +- test/mockupdb/test_handshake.py | 5 +- test/mockupdb/test_initial_ismaster.py | 5 +- test/mockupdb/test_list_indexes.py | 5 +- test/mockupdb/test_max_staleness.py | 5 +- test/mockupdb/test_mixed_version_sharded.py | 5 +- .../mockupdb/test_mongos_command_read_mode.py | 5 +- .../test_network_disconnect_primary.py | 5 +- test/mockupdb/test_op_msg.py | 450 +++++++++--------- test/mockupdb/test_op_msg_read_preference.py | 5 +- test/mockupdb/test_query_read_pref_sharded.py | 5 +- test/mockupdb/test_reset_and_request_check.py | 5 +- test/mockupdb/test_rsghost.py | 5 +- test/mockupdb/test_slave_okay_rs.py | 5 +- test/mockupdb/test_slave_okay_sharded.py | 5 +- test/mockupdb/test_slave_okay_single.py | 5 +- test/mockupdb/test_standalone_shard.py | 5 +- 22 files changed, 374 insertions(+), 303 deletions(-) diff --git a/test/mockupdb/operations.py b/test/mockupdb/operations.py index 458d0228f3..d4d8f53ff8 100644 --- a/test/mockupdb/operations.py +++ b/test/mockupdb/operations.py @@ -17,8 +17,10 @@ try: from mockupdb import OpMsgReply, OpReply + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False from pymongo import ReadPreference @@ -54,67 +56,69 @@ sharded cluster (PYTHON-868). """ -not_master_reply = OpMsgReply(ok=0, errmsg="not master") - -operations = [ - Operation( - "find_one", - lambda client: client.db.collection.find_one(), - reply={"cursor": {"id": 0, "firstBatch": []}}, - op_type="may-use-secondary", - not_master=not_master_reply, - ), - Operation( - "count_documents", - lambda client: client.db.collection.count_documents({}), - reply={"n": 1}, - op_type="may-use-secondary", - not_master=not_master_reply, - ), - Operation( - "estimated_document_count", - lambda client: client.db.collection.estimated_document_count(), - reply={"n": 1}, - op_type="may-use-secondary", - not_master=not_master_reply, - ), - Operation( - "aggregate", - lambda client: client.db.collection.aggregate([]), - reply={"cursor": {"id": 0, "firstBatch": []}}, - op_type="may-use-secondary", - not_master=not_master_reply, - ), - Operation( - "options", - lambda client: client.db.collection.options(), - reply={"cursor": {"id": 0, "firstBatch": []}}, - op_type="must-use-primary", - not_master=not_master_reply, - ), - Operation( - "command", - lambda client: client.db.command("foo"), - reply={"ok": 1}, - op_type="must-use-primary", # Ignores client's read preference. - not_master=not_master_reply, - ), - Operation( - "secondary command", - lambda client: client.db.command("foo", read_preference=ReadPreference.SECONDARY), - reply={"ok": 1}, - op_type="always-use-secondary", - not_master=OpReply(ok=0, errmsg="node is recovering"), - ), - Operation( - "listIndexes", - lambda client: client.db.collection.index_information(), - reply={"cursor": {"id": 0, "firstBatch": []}}, - op_type="must-use-primary", - not_master=not_master_reply, - ), -] - +if _HAVE_MOCKUPDB: + not_master_reply = OpMsgReply(ok=0, errmsg="not master") + + operations = [ + Operation( + "find_one", + lambda client: client.db.collection.find_one(), + reply={"cursor": {"id": 0, "firstBatch": []}}, + op_type="may-use-secondary", + not_master=not_master_reply, + ), + Operation( + "count_documents", + lambda client: client.db.collection.count_documents({}), + reply={"n": 1}, + op_type="may-use-secondary", + not_master=not_master_reply, + ), + Operation( + "estimated_document_count", + lambda client: client.db.collection.estimated_document_count(), + reply={"n": 1}, + op_type="may-use-secondary", + not_master=not_master_reply, + ), + Operation( + "aggregate", + lambda client: client.db.collection.aggregate([]), + reply={"cursor": {"id": 0, "firstBatch": []}}, + op_type="may-use-secondary", + not_master=not_master_reply, + ), + Operation( + "options", + lambda client: client.db.collection.options(), + reply={"cursor": {"id": 0, "firstBatch": []}}, + op_type="must-use-primary", + not_master=not_master_reply, + ), + Operation( + "command", + lambda client: client.db.command("foo"), + reply={"ok": 1}, + op_type="must-use-primary", # Ignores client's read preference. + not_master=not_master_reply, + ), + Operation( + "secondary command", + lambda client: client.db.command("foo", read_preference=ReadPreference.SECONDARY), + reply={"ok": 1}, + op_type="always-use-secondary", + not_master=OpReply(ok=0, errmsg="node is recovering"), + ), + Operation( + "listIndexes", + lambda client: client.db.collection.index_information(), + reply={"cursor": {"id": 0, "firstBatch": []}}, + op_type="must-use-primary", + not_master=not_master_reply, + ), + ] +else: + operations = [] _ops_by_name = {op.name: op for op in operations} diff --git a/test/mockupdb/test_auth_recovering_member.py b/test/mockupdb/test_auth_recovering_member.py index 1ca490b036..95a83a11a7 100644 --- a/test/mockupdb/test_auth_recovering_member.py +++ b/test/mockupdb/test_auth_recovering_member.py @@ -19,8 +19,10 @@ try: from mockupdb import MockupDB + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False from pymongo import MongoClient from pymongo.errors import ServerSelectionTimeoutError diff --git a/test/mockupdb/test_cluster_time.py b/test/mockupdb/test_cluster_time.py index c0bc601d0c..f3ab0a6c54 100644 --- a/test/mockupdb/test_cluster_time.py +++ b/test/mockupdb/test_cluster_time.py @@ -21,8 +21,11 @@ try: from mockupdb import MockupDB, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from bson import Timestamp from pymongo import DeleteMany, InsertOne, MongoClient, UpdateOne diff --git a/test/mockupdb/test_cursor.py b/test/mockupdb/test_cursor.py index 1284d965af..46af39c7b9 100644 --- a/test/mockupdb/test_cursor.py +++ b/test/mockupdb/test_cursor.py @@ -22,8 +22,11 @@ try: from mockupdb import MockupDB, OpMsg, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from bson.objectid import ObjectId from pymongo import MongoClient diff --git a/test/mockupdb/test_cursor_namespace.py b/test/mockupdb/test_cursor_namespace.py index d32abc3f43..57a98373fc 100644 --- a/test/mockupdb/test_cursor_namespace.py +++ b/test/mockupdb/test_cursor_namespace.py @@ -21,8 +21,11 @@ try: from mockupdb import MockupDB, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from pymongo import MongoClient diff --git a/test/mockupdb/test_getmore_sharded.py b/test/mockupdb/test_getmore_sharded.py index c354a69ae7..cf26b10a15 100644 --- a/test/mockupdb/test_getmore_sharded.py +++ b/test/mockupdb/test_getmore_sharded.py @@ -22,8 +22,11 @@ try: from mockupdb import MockupDB, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from pymongo import MongoClient diff --git a/test/mockupdb/test_handshake.py b/test/mockupdb/test_handshake.py index ed621f0f35..19e10f9617 100644 --- a/test/mockupdb/test_handshake.py +++ b/test/mockupdb/test_handshake.py @@ -19,8 +19,11 @@ try: from mockupdb import Command, MockupDB, OpMsg, OpMsgReply, OpQuery, OpReply, absent, go + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from bson.objectid import ObjectId from pymongo import MongoClient diff --git a/test/mockupdb/test_initial_ismaster.py b/test/mockupdb/test_initial_ismaster.py index 1caf65c3a8..8046f08db5 100644 --- a/test/mockupdb/test_initial_ismaster.py +++ b/test/mockupdb/test_initial_ismaster.py @@ -20,8 +20,11 @@ try: from mockupdb import MockupDB, wait_until + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from pymongo import MongoClient diff --git a/test/mockupdb/test_list_indexes.py b/test/mockupdb/test_list_indexes.py index 203b0cc633..ce6db9e104 100644 --- a/test/mockupdb/test_list_indexes.py +++ b/test/mockupdb/test_list_indexes.py @@ -21,8 +21,11 @@ try: from mockupdb import MockupDB, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from bson import SON from pymongo import MongoClient diff --git a/test/mockupdb/test_max_staleness.py b/test/mockupdb/test_max_staleness.py index 4ec5a037ae..40cf7ef00d 100644 --- a/test/mockupdb/test_max_staleness.py +++ b/test/mockupdb/test_max_staleness.py @@ -19,8 +19,11 @@ try: from mockupdb import MockupDB, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from pymongo import MongoClient diff --git a/test/mockupdb/test_mixed_version_sharded.py b/test/mockupdb/test_mixed_version_sharded.py index a656fc6926..72b42deacc 100644 --- a/test/mockupdb/test_mixed_version_sharded.py +++ b/test/mockupdb/test_mixed_version_sharded.py @@ -23,8 +23,11 @@ try: from mockupdb import MockupDB, go + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from operations import upgrades # type: ignore[import] from pymongo import MongoClient diff --git a/test/mockupdb/test_mongos_command_read_mode.py b/test/mockupdb/test_mongos_command_read_mode.py index 952aa1130a..7265c97020 100644 --- a/test/mockupdb/test_mongos_command_read_mode.py +++ b/test/mockupdb/test_mongos_command_read_mode.py @@ -20,8 +20,11 @@ try: from mockupdb import MockupDB, OpMsg, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from operations import operations # type: ignore[import] from pymongo import MongoClient, ReadPreference diff --git a/test/mockupdb/test_network_disconnect_primary.py b/test/mockupdb/test_network_disconnect_primary.py index d36149aee5..32e0471e26 100644 --- a/test/mockupdb/test_network_disconnect_primary.py +++ b/test/mockupdb/test_network_disconnect_primary.py @@ -19,8 +19,11 @@ try: from mockupdb import Future, MockupDB, OpReply, going, wait_until + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from pymongo import MongoClient from pymongo.errors import ConnectionFailure diff --git a/test/mockupdb/test_op_msg.py b/test/mockupdb/test_op_msg.py index 0bd5885c6b..776d1644dd 100644 --- a/test/mockupdb/test_op_msg.py +++ b/test/mockupdb/test_op_msg.py @@ -20,8 +20,10 @@ try: from mockupdb import OP_MSG_FLAGS, MockupDB, OpMsg, OpMsgReply, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False from pymongo import MongoClient, WriteConcern from pymongo.cursor_shared import CursorType @@ -31,238 +33,244 @@ Operation = namedtuple("Operation", ["name", "function", "request", "reply"]) -operations = [ - Operation( - "find_one", - lambda coll: coll.find_one({}), - request=OpMsg({"find": "coll"}, flags=0), - reply={"ok": 1, "cursor": {"firstBatch": [], "id": 0}}, - ), - Operation( - "aggregate", - lambda coll: coll.aggregate([]), - request=OpMsg({"aggregate": "coll"}, flags=0), - reply={"ok": 1, "cursor": {"firstBatch": [], "id": 0}}, - ), - Operation( - "insert_one", - lambda coll: coll.insert_one({}), - request=OpMsg({"insert": "coll"}, flags=0), - reply={"ok": 1, "n": 1}, - ), - Operation( - "insert_one-w0", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).insert_one({}), - request=OpMsg({"insert": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), - reply=None, - ), - Operation( - "insert_many", - lambda coll: coll.insert_many([{}, {}, {}]), - request=OpMsg({"insert": "coll"}, flags=0), - reply={"ok": 1, "n": 3}, - ), - Operation( - "insert_many-w0", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).insert_many([{}, {}, {}]), - request=OpMsg({"insert": "coll"}, flags=0), - reply={"ok": 1, "n": 3}, - ), - Operation( - "insert_many-w0-unordered", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).insert_many( - [{}, {}, {}], ordered=False +if _HAVE_MOCKUPDB: + operations = [ + Operation( + "find_one", + lambda coll: coll.find_one({}), + request=OpMsg({"find": "coll"}, flags=0), + reply={"ok": 1, "cursor": {"firstBatch": [], "id": 0}}, ), - request=OpMsg({"insert": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), - reply=None, - ), - Operation( - "replace_one", - lambda coll: coll.replace_one({"_id": 1}, {"new": 1}), - request=OpMsg({"update": "coll"}, flags=0), - reply={"ok": 1, "n": 1, "nModified": 1}, - ), - Operation( - "replace_one-w0", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).replace_one( - {"_id": 1}, {"new": 1} + Operation( + "aggregate", + lambda coll: coll.aggregate([]), + request=OpMsg({"aggregate": "coll"}, flags=0), + reply={"ok": 1, "cursor": {"firstBatch": [], "id": 0}}, ), - request=OpMsg({"update": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), - reply=None, - ), - Operation( - "update_one", - lambda coll: coll.update_one({"_id": 1}, {"$set": {"new": 1}}), - request=OpMsg({"update": "coll"}, flags=0), - reply={"ok": 1, "n": 1, "nModified": 1}, - ), - Operation( - "replace_one-w0", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).update_one( - {"_id": 1}, {"$set": {"new": 1}} + Operation( + "insert_one", + lambda coll: coll.insert_one({}), + request=OpMsg({"insert": "coll"}, flags=0), + reply={"ok": 1, "n": 1}, ), - request=OpMsg({"update": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), - reply=None, - ), - Operation( - "update_many", - lambda coll: coll.update_many({"_id": 1}, {"$set": {"new": 1}}), - request=OpMsg({"update": "coll"}, flags=0), - reply={"ok": 1, "n": 1, "nModified": 1}, - ), - Operation( - "update_many-w0", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).update_many( - {"_id": 1}, {"$set": {"new": 1}} + Operation( + "insert_one-w0", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).insert_one({}), + request=OpMsg({"insert": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), + reply=None, ), - request=OpMsg({"update": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), - reply=None, - ), - Operation( - "delete_one", - lambda coll: coll.delete_one({"a": 1}), - request=OpMsg({"delete": "coll"}, flags=0), - reply={"ok": 1, "n": 1}, - ), - Operation( - "delete_one-w0", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).delete_one({"a": 1}), - request=OpMsg({"delete": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), - reply=None, - ), - Operation( - "delete_many", - lambda coll: coll.delete_many({"a": 1}), - request=OpMsg({"delete": "coll"}, flags=0), - reply={"ok": 1, "n": 1}, - ), - Operation( - "delete_many-w0", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).delete_many({"a": 1}), - request=OpMsg({"delete": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), - reply=None, - ), - # Legacy methods - Operation( - "bulk_write_insert", - lambda coll: coll.bulk_write([InsertOne[dict]({}), InsertOne[dict]({})]), - request=OpMsg({"insert": "coll"}, flags=0), - reply={"ok": 1, "n": 2}, - ), - Operation( - "bulk_write_insert-w0", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( - [InsertOne[dict]({}), InsertOne[dict]({})] + Operation( + "insert_many", + lambda coll: coll.insert_many([{}, {}, {}]), + request=OpMsg({"insert": "coll"}, flags=0), + reply={"ok": 1, "n": 3}, ), - request=OpMsg({"insert": "coll"}, flags=0), - reply={"ok": 1, "n": 2}, - ), - Operation( - "bulk_write_insert-w0-unordered", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( - [InsertOne[dict]({}), InsertOne[dict]({})], ordered=False + Operation( + "insert_many-w0", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).insert_many( + [{}, {}, {}] + ), + request=OpMsg({"insert": "coll"}, flags=0), + reply={"ok": 1, "n": 3}, ), - request=OpMsg({"insert": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), - reply=None, - ), - Operation( - "bulk_write_update", - lambda coll: coll.bulk_write( - [ - UpdateOne({"_id": 1}, {"$set": {"new": 1}}), - UpdateOne({"_id": 2}, {"$set": {"new": 1}}), - ] + Operation( + "insert_many-w0-unordered", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).insert_many( + [{}, {}, {}], ordered=False + ), + request=OpMsg({"insert": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), + reply=None, ), - request=OpMsg({"update": "coll"}, flags=0), - reply={"ok": 1, "n": 2, "nModified": 2}, - ), - Operation( - "bulk_write_update-w0", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( - [ - UpdateOne({"_id": 1}, {"$set": {"new": 1}}), - UpdateOne({"_id": 2}, {"$set": {"new": 1}}), - ] + Operation( + "replace_one", + lambda coll: coll.replace_one({"_id": 1}, {"new": 1}), + request=OpMsg({"update": "coll"}, flags=0), + reply={"ok": 1, "n": 1, "nModified": 1}, ), - request=OpMsg({"update": "coll"}, flags=0), - reply={"ok": 1, "n": 2, "nModified": 2}, - ), - Operation( - "bulk_write_update-w0-unordered", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( - [ - UpdateOne({"_id": 1}, {"$set": {"new": 1}}), - UpdateOne({"_id": 2}, {"$set": {"new": 1}}), - ], - ordered=False, + Operation( + "replace_one-w0", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).replace_one( + {"_id": 1}, {"new": 1} + ), + request=OpMsg({"update": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), + reply=None, + ), + Operation( + "update_one", + lambda coll: coll.update_one({"_id": 1}, {"$set": {"new": 1}}), + request=OpMsg({"update": "coll"}, flags=0), + reply={"ok": 1, "n": 1, "nModified": 1}, + ), + Operation( + "replace_one-w0", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).update_one( + {"_id": 1}, {"$set": {"new": 1}} + ), + request=OpMsg({"update": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), + reply=None, + ), + Operation( + "update_many", + lambda coll: coll.update_many({"_id": 1}, {"$set": {"new": 1}}), + request=OpMsg({"update": "coll"}, flags=0), + reply={"ok": 1, "n": 1, "nModified": 1}, + ), + Operation( + "update_many-w0", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).update_many( + {"_id": 1}, {"$set": {"new": 1}} + ), + request=OpMsg({"update": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), + reply=None, + ), + Operation( + "delete_one", + lambda coll: coll.delete_one({"a": 1}), + request=OpMsg({"delete": "coll"}, flags=0), + reply={"ok": 1, "n": 1}, + ), + Operation( + "delete_one-w0", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).delete_one({"a": 1}), + request=OpMsg({"delete": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), + reply=None, + ), + Operation( + "delete_many", + lambda coll: coll.delete_many({"a": 1}), + request=OpMsg({"delete": "coll"}, flags=0), + reply={"ok": 1, "n": 1}, + ), + Operation( + "delete_many-w0", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).delete_many({"a": 1}), + request=OpMsg({"delete": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), + reply=None, + ), + # Legacy methods + Operation( + "bulk_write_insert", + lambda coll: coll.bulk_write([InsertOne[dict]({}), InsertOne[dict]({})]), + request=OpMsg({"insert": "coll"}, flags=0), + reply={"ok": 1, "n": 2}, + ), + Operation( + "bulk_write_insert-w0", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( + [InsertOne[dict]({}), InsertOne[dict]({})] + ), + request=OpMsg({"insert": "coll"}, flags=0), + reply={"ok": 1, "n": 2}, + ), + Operation( + "bulk_write_insert-w0-unordered", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( + [InsertOne[dict]({}), InsertOne[dict]({})], ordered=False + ), + request=OpMsg({"insert": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), + reply=None, ), - request=OpMsg({"update": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), - reply=None, - ), - Operation( - "bulk_write_delete", - lambda coll: coll.bulk_write([DeleteOne({"_id": 1}), DeleteOne({"_id": 2})]), - request=OpMsg({"delete": "coll"}, flags=0), - reply={"ok": 1, "n": 2}, - ), - Operation( - "bulk_write_delete-w0", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( - [DeleteOne({"_id": 1}), DeleteOne({"_id": 2})] + Operation( + "bulk_write_update", + lambda coll: coll.bulk_write( + [ + UpdateOne({"_id": 1}, {"$set": {"new": 1}}), + UpdateOne({"_id": 2}, {"$set": {"new": 1}}), + ] + ), + request=OpMsg({"update": "coll"}, flags=0), + reply={"ok": 1, "n": 2, "nModified": 2}, ), - request=OpMsg({"delete": "coll"}, flags=0), - reply={"ok": 1, "n": 2}, - ), - Operation( - "bulk_write_delete-w0-unordered", - lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( - [DeleteOne({"_id": 1}), DeleteOne({"_id": 2})], ordered=False + Operation( + "bulk_write_update-w0", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( + [ + UpdateOne({"_id": 1}, {"$set": {"new": 1}}), + UpdateOne({"_id": 2}, {"$set": {"new": 1}}), + ] + ), + request=OpMsg({"update": "coll"}, flags=0), + reply={"ok": 1, "n": 2, "nModified": 2}, ), - request=OpMsg({"delete": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), - reply=None, - ), -] + Operation( + "bulk_write_update-w0-unordered", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( + [ + UpdateOne({"_id": 1}, {"$set": {"new": 1}}), + UpdateOne({"_id": 2}, {"$set": {"new": 1}}), + ], + ordered=False, + ), + request=OpMsg({"update": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), + reply=None, + ), + Operation( + "bulk_write_delete", + lambda coll: coll.bulk_write([DeleteOne({"_id": 1}), DeleteOne({"_id": 2})]), + request=OpMsg({"delete": "coll"}, flags=0), + reply={"ok": 1, "n": 2}, + ), + Operation( + "bulk_write_delete-w0", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( + [DeleteOne({"_id": 1}), DeleteOne({"_id": 2})] + ), + request=OpMsg({"delete": "coll"}, flags=0), + reply={"ok": 1, "n": 2}, + ), + Operation( + "bulk_write_delete-w0-unordered", + lambda coll: coll.with_options(write_concern=WriteConcern(w=0)).bulk_write( + [DeleteOne({"_id": 1}), DeleteOne({"_id": 2})], ordered=False + ), + request=OpMsg({"delete": "coll"}, flags=OP_MSG_FLAGS["moreToCome"]), + reply=None, + ), + ] -operations_312 = [ - Operation( - "find_raw_batches", - lambda coll: list(coll.find_raw_batches({})), - request=[ - OpMsg({"find": "coll"}, flags=0), - OpMsg({"getMore": 7}, flags=0), - ], - reply=[ - {"ok": 1, "cursor": {"firstBatch": [{}], "id": 7}}, - {"ok": 1, "cursor": {"nextBatch": [{}], "id": 0}}, - ], - ), - Operation( - "aggregate_raw_batches", - lambda coll: list(coll.aggregate_raw_batches([])), - request=[ - OpMsg({"aggregate": "coll"}, flags=0), - OpMsg({"getMore": 7}, flags=0), - ], - reply=[ - {"ok": 1, "cursor": {"firstBatch": [], "id": 7}}, - {"ok": 1, "cursor": {"nextBatch": [{}], "id": 0}}, - ], - ), - Operation( - "find_exhaust_cursor", - lambda coll: list(coll.find({}, cursor_type=CursorType.EXHAUST)), - request=[ - OpMsg({"find": "coll"}, flags=0), - OpMsg({"getMore": 7}, flags=1 << 16), - ], - reply=[ - OpMsgReply({"ok": 1, "cursor": {"firstBatch": [{}], "id": 7}}, flags=0), - OpMsgReply({"ok": 1, "cursor": {"nextBatch": [{}], "id": 7}}, flags=2), - OpMsgReply({"ok": 1, "cursor": {"nextBatch": [{}], "id": 7}}, flags=2), - OpMsgReply({"ok": 1, "cursor": {"nextBatch": [{}], "id": 0}}, flags=0), - ], - ), -] + operations_312 = [ + Operation( + "find_raw_batches", + lambda coll: list(coll.find_raw_batches({})), + request=[ + OpMsg({"find": "coll"}, flags=0), + OpMsg({"getMore": 7}, flags=0), + ], + reply=[ + {"ok": 1, "cursor": {"firstBatch": [{}], "id": 7}}, + {"ok": 1, "cursor": {"nextBatch": [{}], "id": 0}}, + ], + ), + Operation( + "aggregate_raw_batches", + lambda coll: list(coll.aggregate_raw_batches([])), + request=[ + OpMsg({"aggregate": "coll"}, flags=0), + OpMsg({"getMore": 7}, flags=0), + ], + reply=[ + {"ok": 1, "cursor": {"firstBatch": [], "id": 7}}, + {"ok": 1, "cursor": {"nextBatch": [{}], "id": 0}}, + ], + ), + Operation( + "find_exhaust_cursor", + lambda coll: list(coll.find({}, cursor_type=CursorType.EXHAUST)), + request=[ + OpMsg({"find": "coll"}, flags=0), + OpMsg({"getMore": 7}, flags=1 << 16), + ], + reply=[ + OpMsgReply({"ok": 1, "cursor": {"firstBatch": [{}], "id": 7}}, flags=0), + OpMsgReply({"ok": 1, "cursor": {"nextBatch": [{}], "id": 7}}, flags=2), + OpMsgReply({"ok": 1, "cursor": {"nextBatch": [{}], "id": 7}}, flags=2), + OpMsgReply({"ok": 1, "cursor": {"nextBatch": [{}], "id": 0}}, flags=0), + ], + ), + ] +else: + operations = [] + operations_312 = [] class TestOpMsg(unittest.TestCase): diff --git a/test/mockupdb/test_op_msg_read_preference.py b/test/mockupdb/test_op_msg_read_preference.py index ebb1b08143..c7c7037f23 100644 --- a/test/mockupdb/test_op_msg_read_preference.py +++ b/test/mockupdb/test_op_msg_read_preference.py @@ -22,8 +22,11 @@ try: from mockupdb import CommandBase, MockupDB, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from operations import operations # type: ignore[import] from pymongo import MongoClient, ReadPreference diff --git a/test/mockupdb/test_query_read_pref_sharded.py b/test/mockupdb/test_query_read_pref_sharded.py index 62959f651d..6276ee7789 100644 --- a/test/mockupdb/test_query_read_pref_sharded.py +++ b/test/mockupdb/test_query_read_pref_sharded.py @@ -21,8 +21,11 @@ try: from mockupdb import MockupDB, OpMsg, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from bson import SON from pymongo import MongoClient diff --git a/test/mockupdb/test_reset_and_request_check.py b/test/mockupdb/test_reset_and_request_check.py index 46080a4604..2919025f09 100644 --- a/test/mockupdb/test_reset_and_request_check.py +++ b/test/mockupdb/test_reset_and_request_check.py @@ -21,8 +21,11 @@ try: from mockupdb import MockupDB, going, wait_until + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from operations import operations # type: ignore[import] diff --git a/test/mockupdb/test_rsghost.py b/test/mockupdb/test_rsghost.py index c678207f61..84d52170db 100644 --- a/test/mockupdb/test_rsghost.py +++ b/test/mockupdb/test_rsghost.py @@ -22,8 +22,11 @@ try: from mockupdb import MockupDB, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from pymongo import MongoClient from pymongo.errors import ServerSelectionTimeoutError diff --git a/test/mockupdb/test_slave_okay_rs.py b/test/mockupdb/test_slave_okay_rs.py index 94e798f8d8..a60531a716 100644 --- a/test/mockupdb/test_slave_okay_rs.py +++ b/test/mockupdb/test_slave_okay_rs.py @@ -24,8 +24,11 @@ try: from mockupdb import MockupDB, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from operations import operations # type: ignore[import] from pymongo import MongoClient diff --git a/test/mockupdb/test_slave_okay_sharded.py b/test/mockupdb/test_slave_okay_sharded.py index 398155ec5c..252fe772b9 100644 --- a/test/mockupdb/test_slave_okay_sharded.py +++ b/test/mockupdb/test_slave_okay_sharded.py @@ -28,8 +28,11 @@ try: from mockupdb import MockupDB, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from operations import operations # type: ignore[import] from pymongo import MongoClient diff --git a/test/mockupdb/test_slave_okay_single.py b/test/mockupdb/test_slave_okay_single.py index a7b2e6007d..0f562e569a 100644 --- a/test/mockupdb/test_slave_okay_single.py +++ b/test/mockupdb/test_slave_okay_single.py @@ -27,8 +27,11 @@ try: from mockupdb import MockupDB, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from operations import operations # type: ignore[import] from pymongo import MongoClient diff --git a/test/mockupdb/test_standalone_shard.py b/test/mockupdb/test_standalone_shard.py index 0c9dc07ecd..28a582071c 100644 --- a/test/mockupdb/test_standalone_shard.py +++ b/test/mockupdb/test_standalone_shard.py @@ -21,8 +21,11 @@ try: from mockupdb import MockupDB, going + + _HAVE_MOCKUPDB = True except ImportError: - pass + _HAVE_MOCKUPDB = False + from pymongo import MongoClient from pymongo.errors import OperationFailure From 531a10a57ef55655a4614610364a07ca798afecc Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Mon, 22 Jul 2024 16:07:52 -0700 Subject: [PATCH 4/5] Fix mod_wsgi imports --- test/mod_wsgi_test/mod_wsgi_test.py | 129 ++++++++++++++-------------- 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/test/mod_wsgi_test/mod_wsgi_test.py b/test/mod_wsgi_test/mod_wsgi_test.py index d9e6c163dd..4ab2df2442 100644 --- a/test/mod_wsgi_test/mod_wsgi_test.py +++ b/test/mod_wsgi_test/mod_wsgi_test.py @@ -39,73 +39,76 @@ from bson.regex import Regex from pymongo.synchronous.mongo_client import MongoClient -# Ensure the C extensions are installed. -assert bson.has_c() -assert pymongo.has_c() - -OPTS: CodecOptions[dict] = CodecOptions( - uuid_representation=STANDARD, datetime_conversion=DatetimeConversion.DATETIME_AUTO -) -client: MongoClient[dict] = MongoClient() -# Use a unique collection name for each process: -coll_name = f"test-{uuid.uuid4()}" -collection = client.test.get_collection(coll_name, codec_options=OPTS) -ndocs = 20 -collection.drop() -doc = { - "int32": 2 << 15, - "int64": 2 << 50, - "null": None, - "bool": True, - "float": 1.5, - "str": "string", - "list": [1, 2, 3], - "dict": {"a": 1, "b": 2, "c": 3}, - "datetime": datetime.datetime.fromtimestamp(1690328577.446), - "datetime_ms_out_of_range": DatetimeMS(-2 << 60), - "regex_native": re.compile("regex*"), - "regex_pymongo": Regex("regex*"), - "binary": Binary(b"bytes", 128), - "oid": ObjectId(), - "dbref": DBRef("test", 1), - "code": Code("function(){ return true; }"), - "code_w_scope": Code("return function(){ return x; }", scope={"x": False}), - "bytes": b"bytes", - "uuid": uuid.uuid4(), -} -collection.insert_many([dict(i=i, **doc) for i in range(ndocs)]) -client.close() # Discard main thread's request socket. -client = MongoClient() -collection = client.test.get_collection(coll_name, codec_options=OPTS) - try: from mod_wsgi import version as mod_wsgi_version # type: ignore[import] + + _HAVE_MOD_WSGI = True except: mod_wsgi_version = None + _HAVE_MOD_WSGI = False +if _HAVE_MOD_WSGI: + # Ensure the C extensions are installed. + assert bson.has_c() + assert pymongo.has_c() -def application(environ, start_response): - results = list(collection.find().batch_size(10)) - assert len(results) == ndocs, f"n_actual={len(results)} n_expected={ndocs}" - # Test encoding and decoding works (for sub interpreter support). - decoded = bson.decode(bson.encode(doc, codec_options=OPTS), codec_options=OPTS) - for key, value in doc.items(): - # Native regex objects are decoded as bson Regex. - if isinstance(value, re.Pattern): - value = Regex.from_native(value) - assert decoded[key] == value, f"failed on doc[{key!r}]: {decoded[key]!r} != {value!r}" - assert isinstance( - decoded[key], type(value) - ), f"failed on doc[{key}]: {decoded[key]!r} is not an instance of {type(value)}" - - output = ( - f" python {sys.version}, mod_wsgi {mod_wsgi_version}," - f" pymongo {pymongo.version}," - f' mod_wsgi.process_group = {environ["mod_wsgi.process_group"]!r}' - f' mod_wsgi.application_group = {environ["mod_wsgi.application_group"]!r}' - f' wsgi.multithread = {environ["wsgi.multithread"]!r}' - "\n" + OPTS: CodecOptions[dict] = CodecOptions( + uuid_representation=STANDARD, datetime_conversion=DatetimeConversion.DATETIME_AUTO ) - response_headers = [("Content-Length", str(len(output)))] - start_response("200 OK", response_headers) - return [output.encode("ascii")] + client: MongoClient[dict] = MongoClient() + # Use a unique collection name for each process: + coll_name = f"test-{uuid.uuid4()}" + collection = client.test.get_collection(coll_name, codec_options=OPTS) + ndocs = 20 + collection.drop() + doc = { + "int32": 2 << 15, + "int64": 2 << 50, + "null": None, + "bool": True, + "float": 1.5, + "str": "string", + "list": [1, 2, 3], + "dict": {"a": 1, "b": 2, "c": 3}, + "datetime": datetime.datetime.fromtimestamp(1690328577.446), + "datetime_ms_out_of_range": DatetimeMS(-2 << 60), + "regex_native": re.compile("regex*"), + "regex_pymongo": Regex("regex*"), + "binary": Binary(b"bytes", 128), + "oid": ObjectId(), + "dbref": DBRef("test", 1), + "code": Code("function(){ return true; }"), + "code_w_scope": Code("return function(){ return x; }", scope={"x": False}), + "bytes": b"bytes", + "uuid": uuid.uuid4(), + } + collection.insert_many([dict(i=i, **doc) for i in range(ndocs)]) + client.close() # Discard main thread's request socket. + client = MongoClient() + collection = client.test.get_collection(coll_name, codec_options=OPTS) + + def application(environ, start_response): + results = list(collection.find().batch_size(10)) + assert len(results) == ndocs, f"n_actual={len(results)} n_expected={ndocs}" + # Test encoding and decoding works (for sub interpreter support). + decoded = bson.decode(bson.encode(doc, codec_options=OPTS), codec_options=OPTS) + for key, value in doc.items(): + # Native regex objects are decoded as bson Regex. + if isinstance(value, re.Pattern): + value = Regex.from_native(value) + assert decoded[key] == value, f"failed on doc[{key!r}]: {decoded[key]!r} != {value!r}" + assert isinstance( + decoded[key], type(value) + ), f"failed on doc[{key}]: {decoded[key]!r} is not an instance of {type(value)}" + + output = ( + f" python {sys.version}, mod_wsgi {mod_wsgi_version}," + f" pymongo {pymongo.version}," + f' mod_wsgi.process_group = {environ["mod_wsgi.process_group"]!r}' + f' mod_wsgi.application_group = {environ["mod_wsgi.application_group"]!r}' + f' wsgi.multithread = {environ["wsgi.multithread"]!r}' + "\n" + ) + response_headers = [("Content-Length", str(len(output)))] + start_response("200 OK", response_headers) + return [output.encode("ascii")] From d71ebde7f53342b9ef48b7780023f4508faad8d7 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Mon, 22 Jul 2024 16:41:13 -0700 Subject: [PATCH 5/5] Update test-mockupdb command --- hatch.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hatch.toml b/hatch.toml index 281564d400..25d113e5da 100644 --- a/hatch.toml +++ b/hatch.toml @@ -42,7 +42,7 @@ features = ["test"] test = "pytest -v --durations=5 --maxfail=10 {args}" test-eg = "bash ./.evergreen/run-tests.sh {args}" test-async = "test test/asynchronous/ {args}" -test-mockupdb = ["pip install -U git+https://github.com/ajdavis/mongo-mockup-db@master", "test ./test/mockupdb"] +test-mockupdb = ["pip install -U git+https://github.com/ajdavis/mongo-mockup-db@master", "test -m mockupdb"] [envs.encryption] skip-install = true