Merge branch 'master' into PYTHON-4542

This commit is contained in:
Noah Stapp 2026-02-23 14:43:47 -05:00 committed by GitHub
commit df3040d580
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 470 additions and 175 deletions

4
.codecov.yml Normal file
View File

@ -0,0 +1,4 @@
# do not notify until at least 100 builds have been uploaded from the CI pipeline
# you can also set after_n_builds on comments independently
comment:
after_n_builds: 100

View File

@ -38,6 +38,7 @@ post:
# Disabled, causing timeouts
# - func: "upload working dir"
- func: "teardown system"
- func: "upload codecov"
- func: "upload coverage"
- func: "upload mo artifacts"
- func: "upload test results"

View File

@ -250,6 +250,27 @@ functions:
working_dir: src
include_expansions_in_env:
- TOOLCHAIN_VERSION
- COVERAGE
type: test
# Upload coverage codecov
upload codecov:
- command: subprocess.exec
params:
binary: bash
args:
- .evergreen/scripts/upload-codecov.sh
working_dir: src
include_expansions_in_env:
- CODECOV_TOKEN
- build_variant
- task_name
- github_commit
- github_pr_number
- github_pr_head_branch
- github_author
- requester
- branch_name
type: test
# Upload coverage

View File

@ -75,7 +75,7 @@ tasks:
SUB_TEST_NAME: session-creds
TOOLCHAIN_VERSION: 3.14t
tags: [auth-aws, auth-aws-session-creds, free-threaded]
- name: test-auth-aws-rapid-web-identity-python3.14
- name: test-auth-aws-rapid-web-identity-python3.14-cov
commands:
- func: run server
vars:
@ -87,7 +87,8 @@ tasks:
TEST_NAME: auth_aws
SUB_TEST_NAME: web-identity
TOOLCHAIN_VERSION: "3.14"
tags: [auth-aws, auth-aws-web-identity]
COVERAGE: "1"
tags: [auth-aws, auth-aws-web-identity, pr]
- name: test-auth-aws-rapid-web-identity-session-name-python3.14
commands:
- func: run server
@ -904,7 +905,7 @@ tasks:
- ocsp-ecdsa
- rapid
- ocsp-staple
- name: test-ocsp-ecdsa-valid-cert-server-staples-latest-python3.14
- name: test-ocsp-ecdsa-valid-cert-server-staples-latest-python3.14-cov
commands:
- func: run tests
vars:
@ -913,11 +914,13 @@ tasks:
TEST_NAME: ocsp
TOOLCHAIN_VERSION: "3.14"
VERSION: latest
COVERAGE: "1"
tags:
- ocsp
- ocsp-ecdsa
- latest
- ocsp-staple
- pr
- name: test-ocsp-ecdsa-invalid-cert-server-staples-v4.4-python3.10-min-deps
commands:
- func: run tests
@ -1928,7 +1931,7 @@ tasks:
- ocsp-rsa
- rapid
- ocsp-staple
- name: test-ocsp-rsa-valid-cert-server-staples-latest-python3.14
- name: test-ocsp-rsa-valid-cert-server-staples-latest-python3.14-cov
commands:
- func: run tests
vars:
@ -1937,11 +1940,13 @@ tasks:
TEST_NAME: ocsp
TOOLCHAIN_VERSION: "3.14"
VERSION: latest
COVERAGE: "1"
tags:
- ocsp
- ocsp-rsa
- latest
- ocsp-staple
- pr
- name: test-ocsp-rsa-invalid-cert-server-staples-v4.4-python3.10-min-deps
commands:
- func: run tests
@ -2615,20 +2620,18 @@ tasks:
- replica_set-auth-nossl
- async
- free-threaded
- name: test-server-version-python3.13-sync-auth-nossl-replica-set-cov
- name: test-server-version-python3.13-sync-auth-nossl-replica-set
commands:
- func: run server
vars:
AUTH: auth
SSL: nossl
TOPOLOGY: replica_set
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: nossl
TOPOLOGY: replica_set
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.13"
TEST_NAME: default_sync
tags:
@ -2636,20 +2639,18 @@ tasks:
- python-3.13
- replica_set-auth-nossl
- sync
- name: test-server-version-python3.12-async-auth-ssl-replica-set-cov
- name: test-server-version-python3.12-async-auth-ssl-replica-set
commands:
- func: run server
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: replica_set
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: replica_set
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.12"
TEST_NAME: default_async
tags:
@ -2657,20 +2658,18 @@ tasks:
- python-3.12
- replica_set-auth-ssl
- async
- name: test-server-version-python3.11-sync-auth-ssl-replica-set-cov
- name: test-server-version-python3.11-sync-auth-ssl-replica-set
commands:
- func: run server
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: replica_set
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: replica_set
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.11"
TEST_NAME: default_sync
tags:
@ -2743,20 +2742,18 @@ tasks:
- python-pypy3.11
- replica_set-noauth-ssl
- async
- name: test-server-version-python3.14-sync-noauth-ssl-replica-set-cov
- name: test-server-version-python3.14-sync-noauth-ssl-replica-set
commands:
- func: run server
vars:
AUTH: noauth
SSL: ssl
TOPOLOGY: replica_set
COVERAGE: "1"
- func: run tests
vars:
AUTH: noauth
SSL: ssl
TOPOLOGY: replica_set
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.14"
TEST_NAME: default_sync
tags:
@ -2764,20 +2761,18 @@ tasks:
- python-3.14
- replica_set-noauth-ssl
- sync
- name: test-server-version-python3.14-async-auth-nossl-sharded-cluster-cov
- name: test-server-version-python3.14-async-auth-nossl-sharded-cluster
commands:
- func: run server
vars:
AUTH: auth
SSL: nossl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: nossl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.14"
TEST_NAME: default_async
tags:
@ -2829,20 +2824,18 @@ tasks:
- sharded_cluster-auth-ssl
- async
- pr
- name: test-server-version-python3.11-async-auth-ssl-sharded-cluster-cov
- name: test-server-version-python3.11-async-auth-ssl-sharded-cluster
commands:
- func: run server
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.11"
TEST_NAME: default_async
tags:
@ -2850,20 +2843,18 @@ tasks:
- python-3.11
- sharded_cluster-auth-ssl
- async
- name: test-server-version-python3.12-async-auth-ssl-sharded-cluster-cov
- name: test-server-version-python3.12-async-auth-ssl-sharded-cluster
commands:
- func: run server
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.12"
TEST_NAME: default_async
tags:
@ -2871,20 +2862,18 @@ tasks:
- python-3.12
- sharded_cluster-auth-ssl
- async
- name: test-server-version-python3.13-async-auth-ssl-sharded-cluster-cov
- name: test-server-version-python3.13-async-auth-ssl-sharded-cluster
commands:
- func: run server
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.13"
TEST_NAME: default_async
tags:
@ -2892,20 +2881,18 @@ tasks:
- python-3.13
- sharded_cluster-auth-ssl
- async
- name: test-server-version-python3.14-async-auth-ssl-sharded-cluster-cov
- name: test-server-version-python3.14-async-auth-ssl-sharded-cluster
commands:
- func: run server
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.14"
TEST_NAME: default_async
tags:
@ -2976,20 +2963,18 @@ tasks:
- sharded_cluster-auth-ssl
- sync
- pr
- name: test-server-version-python3.11-sync-auth-ssl-sharded-cluster-cov
- name: test-server-version-python3.11-sync-auth-ssl-sharded-cluster
commands:
- func: run server
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.11"
TEST_NAME: default_sync
tags:
@ -2997,20 +2982,18 @@ tasks:
- python-3.11
- sharded_cluster-auth-ssl
- sync
- name: test-server-version-python3.12-sync-auth-ssl-sharded-cluster-cov
- name: test-server-version-python3.12-sync-auth-ssl-sharded-cluster
commands:
- func: run server
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.12"
TEST_NAME: default_sync
tags:
@ -3018,20 +3001,18 @@ tasks:
- python-3.12
- sharded_cluster-auth-ssl
- sync
- name: test-server-version-python3.13-sync-auth-ssl-sharded-cluster-cov
- name: test-server-version-python3.13-sync-auth-ssl-sharded-cluster
commands:
- func: run server
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.13"
TEST_NAME: default_sync
tags:
@ -3039,20 +3020,18 @@ tasks:
- python-3.13
- sharded_cluster-auth-ssl
- sync
- name: test-server-version-python3.14-sync-auth-ssl-sharded-cluster-cov
- name: test-server-version-python3.14-sync-auth-ssl-sharded-cluster
commands:
- func: run server
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.14"
TEST_NAME: default_sync
tags:
@ -3099,20 +3078,18 @@ tasks:
- python-pypy3.11
- sharded_cluster-auth-ssl
- sync
- name: test-server-version-python3.12-async-noauth-nossl-sharded-cluster-cov
- name: test-server-version-python3.12-async-noauth-nossl-sharded-cluster
commands:
- func: run server
vars:
AUTH: noauth
SSL: nossl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
- func: run tests
vars:
AUTH: noauth
SSL: nossl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.12"
TEST_NAME: default_async
tags:
@ -3120,20 +3097,18 @@ tasks:
- python-3.12
- sharded_cluster-noauth-nossl
- async
- name: test-server-version-python3.11-sync-noauth-nossl-sharded-cluster-cov
- name: test-server-version-python3.11-sync-noauth-nossl-sharded-cluster
commands:
- func: run server
vars:
AUTH: noauth
SSL: nossl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
- func: run tests
vars:
AUTH: noauth
SSL: nossl
TOPOLOGY: sharded_cluster
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.11"
TEST_NAME: default_sync
tags:
@ -3141,7 +3116,7 @@ tasks:
- python-3.11
- sharded_cluster-noauth-nossl
- sync
- name: test-server-version-python3.10-async-noauth-ssl-sharded-cluster-min-deps-cov
- name: test-server-version-python3.10-async-noauth-ssl-sharded-cluster-min-deps
commands:
- func: run server
vars:
@ -3149,14 +3124,12 @@ tasks:
SSL: ssl
TOPOLOGY: sharded_cluster
TEST_MIN_DEPS: "1"
COVERAGE: "1"
- func: run tests
vars:
AUTH: noauth
SSL: ssl
TOPOLOGY: sharded_cluster
TEST_MIN_DEPS: "1"
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.10"
TEST_NAME: default_async
tags:
@ -3183,20 +3156,18 @@ tasks:
- python-pypy3.11
- sharded_cluster-noauth-ssl
- sync
- name: test-server-version-python3.13-async-auth-nossl-standalone-cov
- name: test-server-version-python3.13-async-auth-nossl-standalone
commands:
- func: run server
vars:
AUTH: auth
SSL: nossl
TOPOLOGY: standalone
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: nossl
TOPOLOGY: standalone
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.13"
TEST_NAME: default_async
tags:
@ -3204,20 +3175,18 @@ tasks:
- python-3.13
- standalone-auth-nossl
- async
- name: test-server-version-python3.12-sync-auth-nossl-standalone-cov
- name: test-server-version-python3.12-sync-auth-nossl-standalone
commands:
- func: run server
vars:
AUTH: auth
SSL: nossl
TOPOLOGY: standalone
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: nossl
TOPOLOGY: standalone
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.12"
TEST_NAME: default_sync
tags:
@ -3225,20 +3194,18 @@ tasks:
- python-3.12
- standalone-auth-nossl
- sync
- name: test-server-version-python3.11-async-auth-ssl-standalone-cov
- name: test-server-version-python3.11-async-auth-ssl-standalone
commands:
- func: run server
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: standalone
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: standalone
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.11"
TEST_NAME: default_async
tags:
@ -3246,7 +3213,7 @@ tasks:
- python-3.11
- standalone-auth-ssl
- async
- name: test-server-version-python3.10-sync-auth-ssl-standalone-min-deps-cov
- name: test-server-version-python3.10-sync-auth-ssl-standalone-min-deps
commands:
- func: run server
vars:
@ -3254,14 +3221,12 @@ tasks:
SSL: ssl
TOPOLOGY: standalone
TEST_MIN_DEPS: "1"
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: standalone
TEST_MIN_DEPS: "1"
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.10"
TEST_NAME: default_sync
tags:
@ -3293,18 +3258,20 @@ tasks:
- standalone-noauth-nossl
- async
- pr
- name: test-server-version-pypy3.11-sync-noauth-nossl-standalone
- name: test-server-version-pypy3.11-sync-noauth-nossl-standalone-cov
commands:
- func: run server
vars:
AUTH: noauth
SSL: nossl
TOPOLOGY: standalone
COVERAGE: "1"
- func: run tests
vars:
AUTH: noauth
SSL: nossl
TOPOLOGY: standalone
COVERAGE: "1"
TOOLCHAIN_VERSION: pypy3.11
TEST_NAME: default_sync
tags:
@ -3313,20 +3280,18 @@ tasks:
- standalone-noauth-nossl
- sync
- pr
- name: test-server-version-python3.14-async-noauth-ssl-standalone-cov
- name: test-server-version-python3.14-async-noauth-ssl-standalone
commands:
- func: run server
vars:
AUTH: noauth
SSL: ssl
TOPOLOGY: standalone
COVERAGE: "1"
- func: run tests
vars:
AUTH: noauth
SSL: ssl
TOPOLOGY: standalone
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.14"
TEST_NAME: default_async
tags:
@ -4082,7 +4047,7 @@ tasks:
- standalone-noauth-nossl
- async
- pypy
- name: test-standard-latest-python3.12-async-noauth-ssl-replica-set
- name: test-standard-latest-python3.12-async-noauth-ssl-replica-set-cov
commands:
- func: run server
vars:
@ -4090,12 +4055,14 @@ tasks:
SSL: ssl
TOPOLOGY: replica_set
VERSION: latest
COVERAGE: "1"
- func: run tests
vars:
AUTH: noauth
SSL: ssl
TOPOLOGY: replica_set
VERSION: latest
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.12"
TEST_NAME: default_async
tags:
@ -4128,7 +4095,7 @@ tasks:
- replica_set-noauth-ssl
- async
- pypy
- name: test-standard-latest-python3.13-async-auth-ssl-sharded-cluster
- name: test-standard-latest-python3.13-async-auth-ssl-sharded-cluster-cov
commands:
- func: run server
vars:
@ -4136,12 +4103,14 @@ tasks:
SSL: ssl
TOPOLOGY: sharded_cluster
VERSION: latest
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
VERSION: latest
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.13"
TEST_NAME: default_async
tags:
@ -4151,7 +4120,7 @@ tasks:
- sharded_cluster-auth-ssl
- async
- pr
- name: test-standard-latest-python3.11-async-noauth-nossl-standalone
- name: test-standard-latest-python3.11-async-noauth-nossl-standalone-cov
commands:
- func: run server
vars:
@ -4159,12 +4128,14 @@ tasks:
SSL: nossl
TOPOLOGY: standalone
VERSION: latest
COVERAGE: "1"
- func: run tests
vars:
AUTH: noauth
SSL: nossl
TOPOLOGY: standalone
VERSION: latest
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.11"
TEST_NAME: default_async
tags:
@ -4174,7 +4145,7 @@ tasks:
- standalone-noauth-nossl
- async
- pr
- name: test-standard-latest-python3.14-async-noauth-nossl-standalone
- name: test-standard-latest-python3.14-async-noauth-nossl-standalone-cov
commands:
- func: run server
vars:
@ -4182,12 +4153,14 @@ tasks:
SSL: nossl
TOPOLOGY: standalone
VERSION: latest
COVERAGE: "1"
- func: run tests
vars:
AUTH: noauth
SSL: nossl
TOPOLOGY: standalone
VERSION: latest
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.14"
TEST_NAME: default_async
tags:
@ -4829,7 +4802,7 @@ tasks:
- python-3.13
- standalone-noauth-nossl
- noauth
- name: test-non-standard-latest-python3.14t-noauth-ssl-replica-set
- name: test-non-standard-latest-python3.14t-noauth-ssl-replica-set-cov
commands:
- func: run server
vars:
@ -4837,12 +4810,14 @@ tasks:
SSL: ssl
TOPOLOGY: replica_set
VERSION: latest
COVERAGE: "1"
- func: run tests
vars:
AUTH: noauth
SSL: ssl
TOPOLOGY: replica_set
VERSION: latest
COVERAGE: "1"
TOOLCHAIN_VERSION: 3.14t
tags:
- test-non-standard
@ -4874,7 +4849,7 @@ tasks:
- replica_set-noauth-ssl
- noauth
- pypy
- name: test-non-standard-latest-python3.14-auth-ssl-sharded-cluster
- name: test-non-standard-latest-python3.14-auth-ssl-sharded-cluster-cov
commands:
- func: run server
vars:
@ -4882,12 +4857,14 @@ tasks:
SSL: ssl
TOPOLOGY: sharded_cluster
VERSION: latest
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
VERSION: latest
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.14"
tags:
- test-non-standard
@ -4896,7 +4873,7 @@ tasks:
- sharded_cluster-auth-ssl
- auth
- pr
- name: test-non-standard-latest-python3.13-noauth-nossl-standalone
- name: test-non-standard-latest-python3.13-noauth-nossl-standalone-cov
commands:
- func: run server
vars:
@ -4904,12 +4881,14 @@ tasks:
SSL: nossl
TOPOLOGY: standalone
VERSION: latest
COVERAGE: "1"
- func: run tests
vars:
AUTH: noauth
SSL: nossl
TOPOLOGY: standalone
VERSION: latest
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.13"
tags:
- test-non-standard
@ -5007,7 +4986,7 @@ tasks:
- pypy
# Test numpy tests
- name: test-numpy-python3.10
- name: test-numpy-python3.10-python3.10
commands:
- func: test numpy
vars:
@ -5017,16 +4996,18 @@ tasks:
- vector
- python-3.10
- test-numpy
- name: test-numpy-python3.14
- name: test-numpy-python3.14-python3.14-cov
commands:
- func: test numpy
vars:
TOOLCHAIN_VERSION: "3.14"
COVERAGE: "1"
tags:
- binary
- vector
- python-3.14
- test-numpy
- pr
# Test standard auth tests
- name: test-standard-auth-v4.2-python3.10-auth-ssl-sharded-cluster-min-deps
@ -5290,7 +5271,7 @@ tasks:
- sharded_cluster-auth-ssl
- auth
- pypy
- name: test-standard-auth-latest-python3.11-auth-ssl-sharded-cluster
- name: test-standard-auth-latest-python3.11-auth-ssl-sharded-cluster-cov
commands:
- func: run server
vars:
@ -5298,12 +5279,14 @@ tasks:
SSL: ssl
TOPOLOGY: sharded_cluster
VERSION: latest
COVERAGE: "1"
- func: run tests
vars:
AUTH: auth
SSL: ssl
TOPOLOGY: sharded_cluster
VERSION: latest
COVERAGE: "1"
TOOLCHAIN_VERSION: "3.11"
tags:
- test-standard-auth

View File

@ -367,6 +367,8 @@ buildvariants:
display_name: No C Ext RHEL8
run_on:
- rhel87-small
expansions:
NO_EXT: "1"
# No server tests
- name: no-server-rhel8
@ -417,6 +419,8 @@ buildvariants:
run_on:
- ubuntu2204-small
batchtime: 1440
expansions:
COVERAGE: "1"
tags: [pr]
- name: auth-oidc-macos
tasks:

View File

@ -38,6 +38,7 @@ trap "cleanup_tests" SIGINT ERR
# Start the test runner.
echo "Running tests with UV_PYTHON=${UV_PYTHON:-}..."
echo "UV_ARGS=${UV_ARGS}"
uv run ${UV_ARGS} --reinstall-package pymongo .evergreen/scripts/run_tests.py "$@"
echo "Running tests with UV_PYTHON=${UV_PYTHON:-}... done."

View File

@ -321,7 +321,7 @@ def create_no_c_ext_variants():
expansions = dict()
handle_c_ext(C_EXTS[0], expansions)
display_name = get_variant_name("No C Ext", host)
return [create_variant(tasks, display_name, host=host)]
return [create_variant(tasks, display_name, host=host, expansions=expansions)]
def create_mod_wsgi_variants():
@ -344,8 +344,12 @@ def create_test_numpy_tasks():
tasks = []
for python in MIN_MAX_PYTHON:
tags = ["binary", "vector", f"python-{python}", "test-numpy"]
task_name = get_task_name("test-numpy", python=python)
test_func = FunctionCall(func="test numpy", vars=dict(TOOLCHAIN_VERSION=python))
vars = dict(TOOLCHAIN_VERSION=python)
if python == MIN_MAX_PYTHON[-1]:
tags.append("pr")
vars["COVERAGE"] = "1"
task_name = get_task_name("test-numpy", python=python, **vars)
test_func = FunctionCall(func="test numpy", vars=vars)
tasks.append(EvgTask(name=task_name, tags=tags, commands=[test_func]))
return tasks
@ -397,6 +401,7 @@ def create_oidc_auth_variants():
tags=["pr"],
host=host,
batchtime=BATCHTIME_DAY,
expansions=dict(COVERAGE="1"),
)
)
return variants
@ -596,7 +601,7 @@ def create_server_version_tasks():
expansions["TEST_MIN_DEPS"] = "1"
if "t" in python:
tags.append("free-threaded")
if python not in PYPYS and "t" not in python:
if "pr" in tags:
expansions["COVERAGE"] = "1"
name = get_task_name(
"test-server-version",
@ -661,6 +666,8 @@ def create_test_non_standard_tasks():
expansions = dict(AUTH=auth, SSL=ssl, TOPOLOGY=topology, VERSION=version)
if python == ALL_PYTHONS[0]:
expansions["TEST_MIN_DEPS"] = "1"
elif pr:
expansions["COVERAGE"] = "1"
name = get_task_name("test-non-standard", python=python, **expansions)
server_func = FunctionCall(func="run server", vars=expansions)
test_vars = expansions.copy()
@ -703,6 +710,8 @@ def create_test_standard_auth_tasks():
expansions = dict(AUTH=auth, SSL=ssl, TOPOLOGY=topology, VERSION=version)
if python == ALL_PYTHONS[0]:
expansions["TEST_MIN_DEPS"] = "1"
elif pr:
expansions["COVERAGE"] = "1"
name = get_task_name("test-standard-auth", python=python, **expansions)
server_func = FunctionCall(func="run server", vars=expansions)
test_vars = expansions.copy()
@ -741,6 +750,8 @@ def create_standard_tasks():
expansions = dict(AUTH=auth, SSL=ssl, TOPOLOGY=topology, VERSION=version)
if python == ALL_PYTHONS[0]:
expansions["TEST_MIN_DEPS"] = "1"
elif pr:
expansions["COVERAGE"] = "1"
name = get_task_name("test-standard", python=python, sync=sync, **expansions)
server_func = FunctionCall(func="run server", vars=expansions)
test_vars = expansions.copy()
@ -810,8 +821,11 @@ def create_aws_tasks():
if "t" in python:
tags.append("free-threaded")
test_vars = dict(TEST_NAME="auth_aws", SUB_TEST_NAME=test_type, TOOLCHAIN_VERSION=python)
if python == ALL_PYTHONS[0]:
if python == MIN_MAX_PYTHON[0]:
test_vars["TEST_MIN_DEPS"] = "1"
elif python == MIN_MAX_PYTHON[-1]:
tags.append("pr")
test_vars["COVERAGE"] = "1"
name = get_task_name(f"{base_name}-{test_type}", **test_vars)
test_func = FunctionCall(func="run tests", vars=test_vars)
funcs = [server_func, assume_func, test_func]
@ -849,11 +863,11 @@ def create_oidc_tasks():
tasks = []
for sub_test in ["default", "azure", "gcp", "eks", "aks", "gke"]:
vars = dict(TEST_NAME="auth_oidc", SUB_TEST_NAME=sub_test)
test_func = FunctionCall(func="run tests", vars=vars)
task_name = f"test-auth-oidc-{sub_test}"
tags = ["auth_oidc"]
if sub_test != "default":
tags.append("auth_oidc_remote")
test_func = FunctionCall(func="run tests", vars=vars)
task_name = get_task_name(f"test-auth-oidc-{sub_test}", **vars)
tasks.append(EvgTask(name=task_name, tags=tags, commands=[test_func]))
return tasks
@ -903,14 +917,14 @@ def _create_ocsp_tasks(algo, variant, server_type, base_task_name):
)
if python == ALL_PYTHONS[0]:
vars["TEST_MIN_DEPS"] = "1"
test_func = FunctionCall(func="run tests", vars=vars)
tags = ["ocsp", f"ocsp-{algo}", version]
if "disableStapling" not in variant:
tags.append("ocsp-staple")
if algo == "valid-cert-server-staples" and version == "latest":
if base_task_name == "valid-cert-server-staples" and version == "latest":
tags.append("pr")
if "TEST_MIN_DEPS" not in vars:
vars["COVERAGE"] = "1"
test_func = FunctionCall(func="run tests", vars=vars)
task_name = get_task_name(f"test-ocsp-{algo}-{base_task_name}", **vars)
tasks.append(EvgTask(name=task_name, tags=tags, commands=[test_func]))
@ -1077,6 +1091,26 @@ def create_upload_coverage_func():
return "upload coverage", [get_assume_role(), cmd]
def create_upload_coverage_codecov_func():
# Upload the coverage xml report to codecov.
include_expansions = [
"CODECOV_TOKEN",
"build_variant",
"task_name",
"github_commit",
"github_pr_number",
"github_pr_head_branch",
"github_author",
"requester",
"branch_name",
]
args = [
".evergreen/scripts/upload-codecov.sh",
]
upload_cmd = get_subprocess_exec(include_expansions_in_env=include_expansions, args=args)
return "upload codecov", [upload_cmd]
def create_download_and_merge_coverage_func():
include_expansions = ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
args = [
@ -1210,7 +1244,7 @@ def create_run_tests_func():
def create_test_numpy_func():
includes = ["TOOLCHAIN_VERSION"]
includes = ["TOOLCHAIN_VERSION", "COVERAGE"]
test_cmd = get_subprocess_exec(
include_expansions_in_env=includes, args=[".evergreen/just.sh", "test-numpy"]
)

View File

@ -12,7 +12,7 @@ def set_env(name: str, value: Any = "1") -> None:
def start_server():
opts, extra_opts = get_test_options(
"Run a MongoDB server. All given flags will be passed to run-orchestration.sh in DRIVERS_TOOLS.",
"Run a MongoDB server. All given flags will be passed to run-mongodb.sh in DRIVERS_TOOLS.",
require_sub_test_name=False,
allow_extra_opts=True,
)
@ -51,7 +51,7 @@ def start_server():
elif opts.quiet:
extra_opts.append("-q")
cmd = ["bash", f"{DRIVERS_TOOLS}/.evergreen/run-orchestration.sh", *extra_opts]
cmd = ["bash", f"{DRIVERS_TOOLS}/.evergreen/run-mongodb.sh", "start", *extra_opts]
run_command(cmd, cwd=DRIVERS_TOOLS)

View File

@ -4,7 +4,9 @@ import json
import logging
import os
import platform
import shlex
import shutil
import subprocess
import sys
from datetime import datetime
from pathlib import Path
@ -202,6 +204,16 @@ def run() -> None:
if os.environ.get("DEBUG_LOG"):
TEST_ARGS.extend(f"-o log_cli_level={logging.DEBUG}".split())
if os.environ.get("COVERAGE"):
binary = sys.executable.replace(os.sep, "/")
cmd = f"{binary} -m coverage run -m pytest {' '.join(TEST_ARGS)} {' '.join(sys.argv[1:])}"
result = subprocess.run(shlex.split(cmd), check=False) # noqa: S603
cmd = f"{binary} -m coverage report"
subprocess.run(shlex.split(cmd), check=False) # noqa: S603
if result.returncode != 0:
print(result.stderr)
sys.exit(result.returncode)
# Run local tests.
ret = pytest.main(TEST_ARGS + sys.argv[1:])
if ret != 0:

View File

@ -324,7 +324,8 @@ def handle_test_env() -> None:
version = os.environ.get("VERSION", "latest")
cmd = [
"bash",
f"{DRIVERS_TOOLS}/.evergreen/run-orchestration.sh",
f"{DRIVERS_TOOLS}/.evergreen/run-mongodb.sh",
"start",
"--ssl",
"--version",
version,
@ -431,6 +432,9 @@ def handle_test_env() -> None:
# We do not want the default client_context to be initialized.
write_env("DISABLE_CONTEXT")
if test_name == "numpy":
UV_ARGS.append("--with numpy")
if test_name == "perf":
data_dir = ROOT / "specifications/source/benchmarking/data"
if not data_dir.exists():
@ -458,12 +462,14 @@ def handle_test_env() -> None:
# Keep in sync with combine-coverage.sh.
# coverage >=5 is needed for relative_files=true.
UV_ARGS.append("--group coverage")
TEST_ARGS = f"{TEST_ARGS} --cov"
write_env("COVERAGE")
if opts.green_framework:
framework = opts.green_framework or os.environ["GREEN_FRAMEWORK"]
UV_ARGS.append(f"--group {framework}")
if framework == "gevent" and opts.test_min_deps:
# PYTHON-5729. This can be removed when the min supported gevent is moved to 25.9.1.
UV_ARGS.append('--with "setuptools==81.0"')
else:
TEST_ARGS = f"-v --durations=5 {TEST_ARGS}"

View File

@ -1,5 +1,5 @@
#!/bin/bash
# Stop a server that was started using run-orchestration.sh in DRIVERS_TOOLS.
# Stop a server that was started using run-mongodb.sh in DRIVERS_TOOLS.
set -eu
HERE=$(dirname ${BASH_SOURCE:-$0})
@ -11,4 +11,4 @@ if [ -f $HERE/env.sh ]; then
source $HERE/env.sh
fi
bash ${DRIVERS_TOOLS}/.evergreen/stop-orchestration.sh
bash ${DRIVERS_TOOLS}/.evergreen/run-mongodb.sh stop

View File

@ -0,0 +1,57 @@
#!/bin/bash
# shellcheck disable=SC2154
# Upload a coverate report to codecov.
set -eu
HERE=$(dirname ${BASH_SOURCE:-$0})
ROOT=$(dirname "$(dirname $HERE)")
pushd $ROOT > /dev/null
export FNAME=coverage.xml
REQUESTER=${requester:-}
if [ ! -f ".coverage" ]; then
echo "There are no coverage results, not running codecov"
exit 0
fi
if [[ "${REQUESTER}" == "github_pr" || "${REQUESTER}" == "commit" ]]; then
echo "Uploading codecov for $REQUESTER..."
else
echo "Error: requester must be 'github_pr' or 'commit', got '${REQUESTER}'" >&2
exit 1
fi
printf 'sha: %s\n' "$github_commit"
printf 'flag: %s-%s\n' "$build_variant" "$task_name"
printf 'file: %s\n' "$FNAME"
uv tool run --with "coverage[toml]" coverage xml
codecov_args=(
upload-process
--report-type coverage
--disable-search
--fail-on-error
--git-service github
--token "${CODECOV_TOKEN}"
--sha "${github_commit}"
--flag "${build_variant}-${task_name}"
--file "${FNAME}"
)
if [ -n "${github_pr_number:-}" ]; then
printf 'branch: %s:%s\n' "$github_author" "$github_pr_head_branch"
printf 'pr: %s\n' "$github_pr_number"
uv tool run --from codecov-cli codecovcli \
"${codecov_args[@]}" \
--pr "${github_pr_number}" \
--branch "${github_author}:${github_pr_head_branch}"
else
printf 'branch: %s\n' "$branch_name"
uv tool run --from codecov-cli codecovcli \
"${codecov_args[@]}" \
--branch "${branch_name}"
fi
echo "Uploading codecov for $REQUESTER... done."
popd > /dev/null

View File

@ -44,6 +44,7 @@ TEST_SUITE_MAP = {
"mockupdb": "mockupdb",
"ocsp": "ocsp",
"perf": "perf",
"numpy": "",
}
# Tests that require a sub test suite.
@ -51,7 +52,7 @@ SUB_TEST_REQUIRED = ["auth_aws", "auth_oidc", "kms", "mod_wsgi", "perf"]
EXTRA_TESTS = ["mod_wsgi", "aws_lambda", "doctest"]
# Tests that do not use run-orchestration directly.
# Tests that do not use run-mongodb directly.
NO_RUN_ORCHESTRATION = [
"auth_oidc",
"atlas_connect",

View File

@ -26,7 +26,7 @@ jobs:
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7
with:
enable-cache: true
python-version: "3.10"
@ -68,7 +68,7 @@ jobs:
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7
with:
enable-cache: true
python-version: ${{ matrix.python-version }}
@ -79,6 +79,37 @@ jobs:
- name: Run tests
run: uv run --extra test pytest -v
coverage:
# This enables a coverage report for a given PR, which will be augmented by
# the combined codecov report uploaded in Evergreen.
runs-on: ubuntu-latest
name: Coverage
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7
with:
enable-cache: true
python-version: "3.10"
- id: setup-mongodb
uses: mongodb-labs/drivers-evergreen-tools@master
with:
version: "8.0"
- name: Install just
run: uv tool install rust-just
- name: Setup tests
run: COVERAGE=1 just setup-tests
- name: Run tests
run: just run-tests
- name: Generate xml report
run: uv tool run --with "coverage[toml]" coverage xml
- name: Upload test results to Codecov
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
doctest:
runs-on: ubuntu-latest
name: DocTest
@ -87,7 +118,7 @@ jobs:
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7
with:
enable-cache: true
python-version: "3.10"
@ -112,7 +143,7 @@ jobs:
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7
with:
enable-cache: true
python-version: "3.10"
@ -131,7 +162,7 @@ jobs:
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7
with:
enable-cache: true
python-version: "3.10"
@ -153,7 +184,7 @@ jobs:
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7
with:
enable-cache: true
python-version: "${{matrix.python}}"
@ -174,7 +205,7 @@ jobs:
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7
with:
enable-cache: true
python-version: "3.10"
@ -264,7 +295,7 @@ jobs:
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7
with:
python-version: "3.9"
- id: setup-mongodb

View File

@ -18,4 +18,4 @@ jobs:
with:
persist-credentials: false
- name: Run zizmor 🌈
uses: zizmorcore/zizmor-action@135698455da5c3b3e55f73f4419e481ab68cdd95 # v0.4.1
uses: zizmorcore/zizmor-action@0dce2577a4760a2749d8cfb7a84b7d5585ebcb7d # v0.5.0

1
.gitignore vendored
View File

@ -41,4 +41,5 @@ test/lambda/*.json
# test results and logs
xunit-results/
coverage.xml
server.log

View File

@ -197,7 +197,7 @@ the pages will re-render and the browser will automatically refresh.
version of Python, set `UV_PYTHON` before running `just install`.
- Ensure you have started the appropriate Mongo Server(s). You can run `just run-server` with optional args
to set up the server. All given options will be passed to
[`run-orchestration.sh`](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/run-orchestration.sh). Run `$DRIVERS_TOOLS/evergreen/run-orchestration.sh -h`
[`run-mongodb.sh`](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/run-mongodb.sh). Run `$DRIVERS_TOOLS/.evergreen/run-mongodb.sh start -h`
for a full list of options.
- Run `just test` or `pytest` to run all of the tests.
- Append `test/<mod_name>.py::<class_name>::<test_name>` to run
@ -396,7 +396,7 @@ To run any of the test suites with minimum supported dependencies, pass `--test-
- If adding new tests files that should only be run for that test suite, add a pytest marker to the file and add
to the list of pytest markers in `pyproject.toml`. Then add the test suite to the `TEST_SUITE_MAP` in `.evergreen/scripts/utils.py`. If for some reason it is not a pytest-runnable test, add it to the list of `EXTRA_TESTS` instead.
- If the test uses Atlas or otherwise doesn't use `run-orchestration.sh`, add it to the `NO_RUN_ORCHESTRATION` list in
- If the test uses Atlas or otherwise doesn't use `run-mongodb.sh`, add it to the `NO_RUN_ORCHESTRATION` list in
`.evergreen/scripts/utils.py`.
- If there is something special required to run the local server or there is an extra flag that should always be set
like `AUTH`, add that logic to `.evergreen/scripts/run_server.py`.

View File

@ -60,8 +60,9 @@ test *args="-v --durations=5 --maxfail=10": && resync
uv run --extra test python -m pytest {{args}}
[group('test')]
test-numpy: && resync
uv run --extra test --with numpy python -m pytest test/test_bson.py
test-numpy *args="": && resync
just setup-tests numpy {{args}}
just run-tests test/test_bson.py
[group('test')]
run-tests *args: && resync

View File

@ -2841,7 +2841,11 @@ class _ClientConnectionRetryable(Generic[T]):
if self._last_error is None:
self._last_error = exc
if self._server is not None:
if (
self._server is not None
and self._client.topology_description.topology_type_name == "Sharded"
or exc.has_error_label("SystemOverloadedError")
):
self._deprioritized_servers.append(self._server)
def _is_not_eligible_for_retry(self) -> bool:

View File

@ -357,7 +357,7 @@ class SSLContext:
try:
for storename in ("CA", "ROOT"):
self._load_wincerts(storename)
except PermissionError:
except Exception:
# Fall back to certifi
self._load_certifi()
elif _sys.platform == "darwin":

View File

@ -2831,7 +2831,11 @@ class _ClientConnectionRetryable(Generic[T]):
if self._last_error is None:
self._last_error = exc
if self._server is not None:
if (
self._server is not None
and self._client.topology_description.topology_type_name == "Sharded"
or exc.has_error_label("SystemOverloadedError")
):
self._deprioritized_servers.append(self._server)
def _is_not_eligible_for_retry(self) -> bool:

View File

@ -51,7 +51,6 @@ dev = []
pip = ["pip>=20.2"]
gevent = ["gevent>=21.12"]
coverage = [
"pytest-cov>=4.0.0",
"coverage[toml]>=5,<=7.10.7"
]
mockupdb = [
@ -239,7 +238,11 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?)|dummy.*)$"
[tool.coverage.run]
branch = true
source = ["pymongo", "bson", "gridfs" ]
include = [
"pymongo/*",
"bson/*",
"gridfs/*"
]
relative_files = true
[tool.coverage.report]

View File

@ -104,7 +104,7 @@ class OIDCTestBase(AsyncPyMongoTestCase):
@asynccontextmanager
async def fail_point(self, command_args):
cmd_on = SON([("configureFailPoint", "failCommand")])
cmd_on = dict(configureFailPoint="failCommand", appName="auth_oidc")
cmd_on.update(command_args)
client = AsyncMongoClient(self.uri_admin)
await client.admin.command(cmd_on)
@ -112,7 +112,7 @@ class OIDCTestBase(AsyncPyMongoTestCase):
yield
finally:
await client.admin.command(
"configureFailPoint", cmd_on["configureFailPoint"], mode="off"
"configureFailPoint", cmd_on["configureFailPoint"], mode="off", appName="auth_oidc"
)
await client.close()

View File

@ -876,6 +876,8 @@ class TestViews(AsyncEncryptionIntegrationTest):
class TestCorpus(AsyncEncryptionIntegrationTest):
# PYTHON-5708: Encryption tests sending large payloads fail on some mongocryptd versions.
@async_client_context.require_version_max(6, 99)
@unittest.skipUnless(any(AWS_CREDS.values()), "AWS environment credentials are not set")
async def asyncSetUp(self):
await super().asyncSetUp()
@ -1052,6 +1054,8 @@ class TestBsonSizeBatches(AsyncEncryptionIntegrationTest):
client_encrypted: AsyncMongoClient
listener: OvertCommandListener
# PYTHON-5708: Encryption tests sending large payloads fail on some mongocryptd versions.
@async_client_context.require_version_max(6, 99)
async def asyncSetUp(self):
await super().asyncSetUp()
db = async_client_context.client.db

View File

@ -261,6 +261,84 @@ class TestRetryableReads(AsyncIntegrationTest):
self.assertEqual(command_docs[0]["lsid"], command_docs[1]["lsid"])
self.assertIsNot(command_docs[0], command_docs[1])
@async_client_context.require_replica_set
@async_client_context.require_secondaries_count(1)
@async_client_context.require_failCommand_fail_point
@async_client_context.require_version_min(4, 4, 0)
async def test_03_01_retryable_reads_caused_by_overload_errors_are_retried_on_a_different_replicaset_server_when_one_is_available(
self
):
listener = OvertCommandListener()
# 1. Create a client `client` with `retryReads=true`, `readPreference=primaryPreferred`, and command event monitoring enabled.
client = await self.async_rs_or_single_client(
event_listeners=[listener], retryReads=True, readPreference="primaryPreferred"
)
# 2. Configure a fail point with the RetryableError and SystemOverloadedError error labels.
command_args = {
"configureFailPoint": "failCommand",
"mode": {"times": 1},
"data": {
"failCommands": ["find"],
"errorLabels": ["RetryableError", "SystemOverloadedError"],
"errorCode": 6,
},
}
await async_set_fail_point(client, command_args)
# 3. Reset the command event monitor to clear the fail point command from its stored events.
listener.reset()
# 4. Execute a `find` command with `client`.
await client.t.t.find_one({})
# 5. Assert that one failed command event and one successful command event occurred.
self.assertEqual(len(listener.failed_events), 1)
self.assertEqual(len(listener.succeeded_events), 1)
# 6. Assert that both events occurred on different servers.
assert listener.failed_events[0].connection_id != listener.succeeded_events[0].connection_id
@async_client_context.require_replica_set
@async_client_context.require_secondaries_count(1)
@async_client_context.require_failCommand_fail_point
@async_client_context.require_version_min(4, 4, 0)
async def test_03_02_retryable_reads_caused_by_non_overload_errors_are_retried_on_the_same_replicaset_server(
self
):
listener = OvertCommandListener()
# 1. Create a client `client` with `retryReads=true`, `readPreference=primaryPreferred`, and command event monitoring enabled.
client = await self.async_rs_or_single_client(
event_listeners=[listener], retryReads=True, readPreference="primaryPreferred"
)
# 2. Configure a fail point with the RetryableError error label.
command_args = {
"configureFailPoint": "failCommand",
"mode": {"times": 1},
"data": {
"failCommands": ["find"],
"errorLabels": ["RetryableError"],
"errorCode": 6,
},
}
await async_set_fail_point(client, command_args)
# 3. Reset the command event monitor to clear the fail point command from its stored events.
listener.reset()
# 4. Execute a `find` command with `client`.
await client.t.t.find_one({})
# 5. Assert that one failed command event and one successful command event occurred.
self.assertEqual(len(listener.failed_events), 1)
self.assertEqual(len(listener.succeeded_events), 1)
# 6. Assert that both events occurred the same server.
assert listener.failed_events[0].connection_id == listener.succeeded_events[0].connection_id
if __name__ == "__main__":
unittest.main()

View File

@ -48,19 +48,11 @@ from pymongo.write_concern import WriteConcern
_HAVE_PYOPENSSL = False
try:
# All of these must be available to use PyOpenSSL
import OpenSSL
import requests
import service_identity
# Ensure service_identity>=18.1 is installed
from service_identity.pyopenssl import verify_ip_address
from pymongo.ocsp_support import _load_trusted_ca_certs
from pymongo import pyopenssl_context
_HAVE_PYOPENSSL = True
except ImportError:
_load_trusted_ca_certs = None # type: ignore
pass
if HAVE_SSL:
@ -136,11 +128,6 @@ class TestClientSSL(AsyncPyMongoTestCase):
def test_use_pyopenssl_when_available(self):
self.assertTrue(HAVE_PYSSL)
@unittest.skipUnless(_HAVE_PYOPENSSL, "Cannot test without PyOpenSSL")
def test_load_trusted_ca_certs(self):
trusted_ca_certs = _load_trusted_ca_certs(CA_BUNDLE_PEM)
self.assertEqual(2, len(trusted_ca_certs))
class TestSSL(AsyncIntegrationTest):
saved_port: int

View File

@ -25,7 +25,8 @@
"$$placeholder": 1
},
"retryReads": false,
"retryWrites": false
"retryWrites": false,
"appName": "mongodb-oidc-no-retry"
},
"observeEvents": [
"commandStartedEvent",
@ -147,7 +148,8 @@
"failCommands": [
"find"
],
"errorCode": 391
"errorCode": 391,
"appName": "mongodb-oidc-no-retry"
}
}
}
@ -212,7 +214,8 @@
"failCommands": [
"insert"
],
"errorCode": 391
"errorCode": 391,
"appName": "mongodb-oidc-no-retry"
}
}
}
@ -289,7 +292,8 @@
"failCommands": [
"insert"
],
"closeConnection": true
"closeConnection": true,
"appName": "mongodb-oidc-no-retry"
}
}
}
@ -321,7 +325,8 @@
"failCommands": [
"saslStart"
],
"errorCode": 18
"errorCode": 18,
"appName": "mongodb-oidc-no-retry"
}
}
}
@ -398,7 +403,8 @@
"failCommands": [
"saslStart"
],
"errorCode": 18
"errorCode": 18,
"appName": "mongodb-oidc-no-retry"
}
}
}

View File

@ -104,14 +104,16 @@ class OIDCTestBase(PyMongoTestCase):
@contextmanager
def fail_point(self, command_args):
cmd_on = SON([("configureFailPoint", "failCommand")])
cmd_on = dict(configureFailPoint="failCommand", appName="auth_oidc")
cmd_on.update(command_args)
client = MongoClient(self.uri_admin)
client.admin.command(cmd_on)
try:
yield
finally:
client.admin.command("configureFailPoint", cmd_on["configureFailPoint"], mode="off")
client.admin.command(
"configureFailPoint", cmd_on["configureFailPoint"], mode="off", appName="auth_oidc"
)
client.close()

View File

@ -872,6 +872,8 @@ class TestViews(EncryptionIntegrationTest):
class TestCorpus(EncryptionIntegrationTest):
# PYTHON-5708: Encryption tests sending large payloads fail on some mongocryptd versions.
@client_context.require_version_max(6, 99)
@unittest.skipUnless(any(AWS_CREDS.values()), "AWS environment credentials are not set")
def setUp(self):
super().setUp()
@ -1048,6 +1050,8 @@ class TestBsonSizeBatches(EncryptionIntegrationTest):
client_encrypted: MongoClient
listener: OvertCommandListener
# PYTHON-5708: Encryption tests sending large payloads fail on some mongocryptd versions.
@client_context.require_version_max(6, 99)
def setUp(self):
super().setUp()
db = client_context.client.db

View File

@ -259,6 +259,84 @@ class TestRetryableReads(IntegrationTest):
self.assertEqual(command_docs[0]["lsid"], command_docs[1]["lsid"])
self.assertIsNot(command_docs[0], command_docs[1])
@client_context.require_replica_set
@client_context.require_secondaries_count(1)
@client_context.require_failCommand_fail_point
@client_context.require_version_min(4, 4, 0)
def test_03_01_retryable_reads_caused_by_overload_errors_are_retried_on_a_different_replicaset_server_when_one_is_available(
self
):
listener = OvertCommandListener()
# 1. Create a client `client` with `retryReads=true`, `readPreference=primaryPreferred`, and command event monitoring enabled.
client = self.rs_or_single_client(
event_listeners=[listener], retryReads=True, readPreference="primaryPreferred"
)
# 2. Configure a fail point with the RetryableError and SystemOverloadedError error labels.
command_args = {
"configureFailPoint": "failCommand",
"mode": {"times": 1},
"data": {
"failCommands": ["find"],
"errorLabels": ["RetryableError", "SystemOverloadedError"],
"errorCode": 6,
},
}
set_fail_point(client, command_args)
# 3. Reset the command event monitor to clear the fail point command from its stored events.
listener.reset()
# 4. Execute a `find` command with `client`.
client.t.t.find_one({})
# 5. Assert that one failed command event and one successful command event occurred.
self.assertEqual(len(listener.failed_events), 1)
self.assertEqual(len(listener.succeeded_events), 1)
# 6. Assert that both events occurred on different servers.
assert listener.failed_events[0].connection_id != listener.succeeded_events[0].connection_id
@client_context.require_replica_set
@client_context.require_secondaries_count(1)
@client_context.require_failCommand_fail_point
@client_context.require_version_min(4, 4, 0)
def test_03_02_retryable_reads_caused_by_non_overload_errors_are_retried_on_the_same_replicaset_server(
self
):
listener = OvertCommandListener()
# 1. Create a client `client` with `retryReads=true`, `readPreference=primaryPreferred`, and command event monitoring enabled.
client = self.rs_or_single_client(
event_listeners=[listener], retryReads=True, readPreference="primaryPreferred"
)
# 2. Configure a fail point with the RetryableError error label.
command_args = {
"configureFailPoint": "failCommand",
"mode": {"times": 1},
"data": {
"failCommands": ["find"],
"errorLabels": ["RetryableError"],
"errorCode": 6,
},
}
set_fail_point(client, command_args)
# 3. Reset the command event monitor to clear the fail point command from its stored events.
listener.reset()
# 4. Execute a `find` command with `client`.
client.t.t.find_one({})
# 5. Assert that one failed command event and one successful command event occurred.
self.assertEqual(len(listener.failed_events), 1)
self.assertEqual(len(listener.succeeded_events), 1)
# 6. Assert that both events occurred the same server.
assert listener.failed_events[0].connection_id == listener.succeeded_events[0].connection_id
if __name__ == "__main__":
unittest.main()

View File

@ -48,19 +48,11 @@ from pymongo.write_concern import WriteConcern
_HAVE_PYOPENSSL = False
try:
# All of these must be available to use PyOpenSSL
import OpenSSL
import requests
import service_identity
# Ensure service_identity>=18.1 is installed
from service_identity.pyopenssl import verify_ip_address
from pymongo.ocsp_support import _load_trusted_ca_certs
from pymongo import pyopenssl_context
_HAVE_PYOPENSSL = True
except ImportError:
_load_trusted_ca_certs = None # type: ignore
pass
if HAVE_SSL:
@ -136,11 +128,6 @@ class TestClientSSL(PyMongoTestCase):
def test_use_pyopenssl_when_available(self):
self.assertTrue(HAVE_PYSSL)
@unittest.skipUnless(_HAVE_PYOPENSSL, "Cannot test without PyOpenSSL")
def test_load_trusted_ca_certs(self):
trusted_ca_certs = _load_trusted_ca_certs(CA_BUNDLE_PEM)
self.assertEqual(2, len(trusted_ca_certs))
class TestSSL(IntegrationTest):
saved_port: int

21
uv.lock generated
View File

@ -1562,7 +1562,6 @@ zstd = [
[package.dev-dependencies]
coverage = [
{ name = "coverage", extra = ["toml"] },
{ name = "pytest-cov" },
]
gevent = [
{ name = "gevent" },
@ -1612,10 +1611,7 @@ requires-dist = [
provides-extras = ["aws", "docs", "encryption", "gssapi", "ocsp", "snappy", "test", "zstd"]
[package.metadata.requires-dev]
coverage = [
{ name = "coverage", extras = ["toml"], specifier = ">=5,<=7.10.7" },
{ name = "pytest-cov", specifier = ">=4.0.0" },
]
coverage = [{ name = "coverage", extras = ["toml"], specifier = ">=5,<=7.10.7" }]
dev = []
gevent = [{ name = "gevent", specifier = ">=21.12" }]
mockupdb = [{ name = "mockupdb", git = "https://github.com/mongodb-labs/mongo-mockup-db?rev=master" }]
@ -1763,21 +1759,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" },
]
[[package]]
name = "pytest-cov"
version = "7.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "coverage", extra = ["toml"] },
{ name = "pluggy" },
{ name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" },
]
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"