Merge branch 'master' of github.com:mongodb/mongo-python-driver
This commit is contained in:
commit
017e1ef9e0
@ -34,51 +34,14 @@ functions:
|
||||
# Applies the subitted patch, if any
|
||||
# Deprecated. Should be removed. But still needed for certain agents (ZAP)
|
||||
- command: git.apply_patch
|
||||
# Make an evergreen exapanstion file with dynamic values
|
||||
- command: shell.exec
|
||||
# Make an evergreen expansion file with dynamic values
|
||||
- command: subprocess.exec
|
||||
params:
|
||||
include_expansions_in_env: ["is_patch", "project", "version_id"]
|
||||
binary: bash
|
||||
working_dir: "src"
|
||||
script: |
|
||||
set +x
|
||||
# Get the current unique version of this checkout
|
||||
if [ "${is_patch}" = "true" ]; then
|
||||
CURRENT_VERSION=$(git describe)-patch-${version_id}
|
||||
else
|
||||
CURRENT_VERSION=latest
|
||||
fi
|
||||
|
||||
export DRIVERS_TOOLS="$(dirname $(pwd))/drivers-tools"
|
||||
export PROJECT_DIRECTORY="$(pwd)"
|
||||
|
||||
# Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory
|
||||
if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
|
||||
export DRIVERS_TOOLS=$(cygpath -m $DRIVERS_TOOLS)
|
||||
export PROJECT_DIRECTORY=$(cygpath -m $PROJECT_DIRECTORY)
|
||||
fi
|
||||
|
||||
export MONGO_ORCHESTRATION_HOME="$DRIVERS_TOOLS/.evergreen/orchestration"
|
||||
export MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin"
|
||||
|
||||
cat <<EOT > expansion.yml
|
||||
CURRENT_VERSION: "$CURRENT_VERSION"
|
||||
DRIVERS_TOOLS: "$DRIVERS_TOOLS"
|
||||
MONGO_ORCHESTRATION_HOME: "$MONGO_ORCHESTRATION_HOME"
|
||||
MONGODB_BINARIES: "$MONGODB_BINARIES"
|
||||
PROJECT_DIRECTORY: "$PROJECT_DIRECTORY"
|
||||
PREPARE_SHELL: |
|
||||
set -o errexit
|
||||
export SKIP_LEGACY_SHELL=1
|
||||
export DRIVERS_TOOLS="$DRIVERS_TOOLS"
|
||||
export MONGO_ORCHESTRATION_HOME="$MONGO_ORCHESTRATION_HOME"
|
||||
export MONGODB_BINARIES="$MONGODB_BINARIES"
|
||||
export PROJECT_DIRECTORY="$PROJECT_DIRECTORY"
|
||||
|
||||
export TMPDIR="$MONGO_ORCHESTRATION_HOME/db"
|
||||
export PATH="$MONGODB_BINARIES:$PATH"
|
||||
export PROJECT="${project}"
|
||||
export PIP_QUIET=1
|
||||
EOT
|
||||
|
||||
args:
|
||||
- .evergreen/scripts/configure-env.sh
|
||||
# Load the expansion file to make an evergreen variable with the current unique version
|
||||
- command: expansions.update
|
||||
params:
|
||||
@ -88,14 +51,14 @@ functions:
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
set -o xtrace
|
||||
rm -rf $DRIVERS_TOOLS
|
||||
if [ "${project}" = "drivers-tools" ]; then
|
||||
if [ "$PROJECT" = "drivers-tools" ]; then
|
||||
# If this was a patch build, doing a fresh clone would not actually test the patch
|
||||
cp -R ${PROJECT_DIRECTORY}/ $DRIVERS_TOOLS
|
||||
cp -R ${PROJECT_DIRECTORY}/ ${DRIVERS_TOOLS}
|
||||
else
|
||||
git clone https://github.com/mongodb-labs/drivers-evergreen-tools.git $DRIVERS_TOOLS
|
||||
git clone https://github.com/mongodb-labs/drivers-evergreen-tools.git ${DRIVERS_TOOLS}
|
||||
fi
|
||||
echo "{ \"releases\": { \"default\": \"$MONGODB_BINARIES\" }}" > $MONGO_ORCHESTRATION_HOME/orchestration.config
|
||||
|
||||
@ -129,12 +92,12 @@ functions:
|
||||
script: |
|
||||
# Download all the task coverage files.
|
||||
aws s3 cp --recursive s3://${bucket_name}/coverage/${revision}/${version_id}/coverage/ coverage/
|
||||
- command: shell.exec
|
||||
- command: subprocess.exec
|
||||
params:
|
||||
working_dir: "src"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
bash .evergreen/combine-coverage.sh
|
||||
binary: bash
|
||||
args:
|
||||
- .evergreen/combine-coverage.sh
|
||||
# Upload the resulting html coverage report.
|
||||
- command: shell.exec
|
||||
params:
|
||||
@ -164,7 +127,7 @@ functions:
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
set -o xtrace
|
||||
mkdir out_dir
|
||||
find $MONGO_ORCHESTRATION_HOME -name \*.log -exec sh -c 'x="{}"; mv $x $PWD/out_dir/$(basename $(dirname $x))_$(basename $x)' \;
|
||||
@ -266,7 +229,7 @@ functions:
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
set -o xtrace
|
||||
|
||||
# Enable core dumps if enabled on the machine
|
||||
@ -325,13 +288,13 @@ functions:
|
||||
type: setup
|
||||
params:
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
bash ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/pull-mongohouse-image.sh
|
||||
- command: shell.exec
|
||||
type: setup
|
||||
params:
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
bash ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/run-mongohouse-image.sh
|
||||
sleep 1
|
||||
docker ps
|
||||
@ -340,7 +303,7 @@ functions:
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
set -o xtrace
|
||||
bash ${DRIVERS_TOOLS}/.evergreen/stop-orchestration.sh
|
||||
|
||||
@ -350,7 +313,7 @@ functions:
|
||||
params:
|
||||
working_dir: "src"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
set -o xtrace
|
||||
PYTHON_BINARY=${PYTHON_BINARY} MOD_WSGI_VERSION=${MOD_WSGI_VERSION} \
|
||||
MOD_WSGI_EMBEDDED=${MOD_WSGI_EMBEDDED} PROJECT_DIRECTORY=${PROJECT_DIRECTORY} \
|
||||
@ -362,7 +325,7 @@ functions:
|
||||
params:
|
||||
working_dir: "src"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
set -o xtrace
|
||||
export PYTHON_BINARY=${PYTHON_BINARY}
|
||||
bash ${PROJECT_DIRECTORY}/.evergreen/hatch.sh test:test-mockupdb
|
||||
@ -373,7 +336,7 @@ functions:
|
||||
params:
|
||||
working_dir: "src"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
set -o xtrace
|
||||
PYTHON_BINARY=${PYTHON_BINARY} bash ${PROJECT_DIRECTORY}/.evergreen/hatch.sh doctest:test
|
||||
|
||||
@ -385,7 +348,7 @@ functions:
|
||||
background: true
|
||||
include_expansions_in_env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
if [ -n "${test_encryption}" ]; then
|
||||
./.evergreen/hatch.sh encryption:setup
|
||||
fi
|
||||
@ -397,7 +360,7 @@ functions:
|
||||
script: |
|
||||
# Disable xtrace
|
||||
set +x
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
if [ -n "${MONGODB_STARTED}" ]; then
|
||||
export PYMONGO_MUST_CONNECT=true
|
||||
fi
|
||||
@ -447,6 +410,7 @@ functions:
|
||||
SSL=${SSL} \
|
||||
TEST_DATA_LAKE=${TEST_DATA_LAKE} \
|
||||
MONGODB_API_VERSION=${MONGODB_API_VERSION} \
|
||||
SKIP_HATCH=${SKIP_HATCH} \
|
||||
bash ${PROJECT_DIRECTORY}/.evergreen/hatch.sh test:test-eg
|
||||
|
||||
"run enterprise auth tests":
|
||||
@ -497,7 +461,7 @@ functions:
|
||||
shell: "bash"
|
||||
working_dir: "src"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
.evergreen/run-mongodb-aws-test.sh regular
|
||||
|
||||
"run aws auth test with assume role credentials":
|
||||
@ -507,7 +471,7 @@ functions:
|
||||
shell: "bash"
|
||||
working_dir: "src"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
.evergreen/run-mongodb-aws-test.sh assume-role
|
||||
|
||||
"run aws auth test with aws EC2 credentials":
|
||||
@ -521,7 +485,7 @@ functions:
|
||||
echo "This platform does not support the EC2 auth test, skipping..."
|
||||
exit 0
|
||||
fi
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
.evergreen/run-mongodb-aws-test.sh ec2
|
||||
|
||||
"run aws auth test with aws web identity credentials":
|
||||
@ -535,7 +499,7 @@ functions:
|
||||
echo "This platform does not support the web identity auth test, skipping..."
|
||||
exit 0
|
||||
fi
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
# Test with and without AWS_ROLE_SESSION_NAME set.
|
||||
.evergreen/run-mongodb-aws-test.sh web-identity
|
||||
AWS_ROLE_SESSION_NAME="test" \
|
||||
@ -558,7 +522,7 @@ functions:
|
||||
working_dir: "src"
|
||||
shell: bash
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
.evergreen/run-mongodb-aws-test.sh env-creds
|
||||
|
||||
"run aws auth test with aws credentials and session token as environment variables":
|
||||
@ -568,7 +532,7 @@ functions:
|
||||
working_dir: "src"
|
||||
shell: bash
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
.evergreen/run-mongodb-aws-test.sh session-creds
|
||||
|
||||
"run aws ECS auth test":
|
||||
@ -582,12 +546,12 @@ functions:
|
||||
echo "This platform does not support the ECS auth test, skipping..."
|
||||
exit 0
|
||||
fi
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
set -ex
|
||||
cd ${DRIVERS_TOOLS}/.evergreen/auth_aws
|
||||
. ./activate-authawsvenv.sh
|
||||
. aws_setup.sh ecs
|
||||
export MONGODB_BINARIES="${MONGODB_BINARIES}";
|
||||
export MONGODB_BINARIES="$MONGODB_BINARIES";
|
||||
export PROJECT_DIRECTORY="${PROJECT_DIRECTORY}";
|
||||
python aws_tester.py ecs
|
||||
cd -
|
||||
@ -597,9 +561,11 @@ functions:
|
||||
params:
|
||||
working_dir: "src"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/hatch.sh encryption:teardown
|
||||
rm -rf $DRIVERS_TOOLS || true
|
||||
. .evergreen/scripts/env.sh
|
||||
if [ -f $DRIVERS_TOOLS/.evergreen/csfle/secrets-export.sh ]; then
|
||||
. .evergreen/hatch.sh encryption:teardown
|
||||
fi
|
||||
rm -rf ${DRIVERS_TOOLS} || true
|
||||
rm -f ./secrets-export.sh || true
|
||||
|
||||
"fix absolute paths":
|
||||
@ -607,7 +573,7 @@ functions:
|
||||
params:
|
||||
script: |
|
||||
set +x
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
for filename in $(find ${DRIVERS_TOOLS} -name \*.json); do
|
||||
perl -p -i -e "s|ABSOLUTE_PATH_REPLACEMENT_TOKEN|${DRIVERS_TOOLS}|g" $filename
|
||||
done
|
||||
@ -617,20 +583,20 @@ functions:
|
||||
params:
|
||||
script: |
|
||||
set +x
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
for i in $(find ${DRIVERS_TOOLS}/.evergreen ${PROJECT_DIRECTORY}/.evergreen -name \*.sh); do
|
||||
cat $i | tr -d '\r' > $i.new
|
||||
mv $i.new $i
|
||||
done
|
||||
# Copy client certificate because symlinks do not work on Windows.
|
||||
cp ${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem ${MONGO_ORCHESTRATION_HOME}/lib/client.pem
|
||||
cp ${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem $MONGO_ORCHESTRATION_HOME/lib/client.pem
|
||||
|
||||
"make files executable":
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: |
|
||||
set +x
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
for i in $(find ${DRIVERS_TOOLS}/.evergreen ${PROJECT_DIRECTORY}/.evergreen -name \*.sh); do
|
||||
chmod +x $i
|
||||
done
|
||||
@ -640,7 +606,7 @@ functions:
|
||||
params:
|
||||
script: |
|
||||
set +x
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
echo '{"results": [{ "status": "FAIL", "test_file": "Build", "log_raw": "No test-results.json found was created" } ]}' > ${PROJECT_DIRECTORY}/test-results.json
|
||||
|
||||
"install dependencies":
|
||||
@ -648,7 +614,7 @@ functions:
|
||||
params:
|
||||
working_dir: "src"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
set -o xtrace
|
||||
file="${PROJECT_DIRECTORY}/.evergreen/install-dependencies.sh"
|
||||
# Don't use ${file} syntax here because evergreen treats it as an empty expansion.
|
||||
@ -679,10 +645,10 @@ functions:
|
||||
params:
|
||||
working_dir: "src"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
TEST_OCSP=1 \
|
||||
PYTHON_BINARY=${PYTHON_BINARY} \
|
||||
CA_FILE="$DRIVERS_TOOLS/.evergreen/ocsp/${OCSP_ALGORITHM}/ca.pem" \
|
||||
CA_FILE="${DRIVERS_TOOLS}/.evergreen/ocsp/${OCSP_ALGORITHM}/ca.pem" \
|
||||
OCSP_TLS_SHOULD_SUCCEED="${OCSP_TLS_SHOULD_SUCCEED}" \
|
||||
bash ${PROJECT_DIRECTORY}/.evergreen/hatch.sh test:test-eg
|
||||
|
||||
@ -691,7 +657,7 @@ functions:
|
||||
params:
|
||||
background: true
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
cd ${DRIVERS_TOOLS}/.evergreen/ocsp
|
||||
. ./activate-ocspvenv.sh
|
||||
python ocsp_mock.py \
|
||||
@ -704,7 +670,7 @@ functions:
|
||||
params:
|
||||
background: true
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
cd ${DRIVERS_TOOLS}/.evergreen/ocsp
|
||||
. ./activate-ocspvenv.sh
|
||||
python ocsp_mock.py \
|
||||
@ -719,7 +685,7 @@ functions:
|
||||
params:
|
||||
background: true
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
cd ${DRIVERS_TOOLS}/.evergreen/ocsp
|
||||
. ./activate-ocspvenv.sh
|
||||
python ocsp_mock.py \
|
||||
@ -732,7 +698,7 @@ functions:
|
||||
params:
|
||||
background: true
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
cd ${DRIVERS_TOOLS}/.evergreen/ocsp
|
||||
. ./activate-ocspvenv.sh
|
||||
python ocsp_mock.py \
|
||||
@ -774,7 +740,7 @@ functions:
|
||||
params:
|
||||
shell: "bash"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
cd "${DRIVERS_TOOLS}/.evergreen/auth_aws"
|
||||
if [ -f "./aws_e2e_setup.json" ]; then
|
||||
. ./activate-authawsvenv.sh
|
||||
@ -794,7 +760,7 @@ functions:
|
||||
params:
|
||||
working_dir: "src"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
PROJECT_DIRECTORY=${PROJECT_DIRECTORY} bash ${PROJECT_DIRECTORY}/.evergreen/run-perf-tests.sh
|
||||
|
||||
"attach benchmark test results":
|
||||
@ -1889,7 +1855,7 @@ tasks:
|
||||
shell: bash
|
||||
script: |-
|
||||
set -o errexit
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
cd src
|
||||
git add .
|
||||
git commit -m "add files"
|
||||
@ -1906,7 +1872,7 @@ tasks:
|
||||
shell: bash
|
||||
script: |-
|
||||
set -o errexit
|
||||
${PREPARE_SHELL}
|
||||
. src/.evergreen/scripts/env.sh
|
||||
cd src
|
||||
git add .
|
||||
git commit -m "add files"
|
||||
@ -1974,7 +1940,7 @@ tasks:
|
||||
working_dir: "src"
|
||||
shell: "bash"
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
export PYTHON_BINARY=/opt/mongodbtoolchain/v4/bin/python3
|
||||
export LIBMONGOCRYPT_URL=https://s3.amazonaws.com/mciuploads/libmongocrypt/debian11/master/latest/libmongocrypt.tar.gz
|
||||
SKIP_SERVERS=1 bash ./.evergreen/setup-encryption.sh
|
||||
@ -2051,7 +2017,7 @@ tasks:
|
||||
shell: "bash"
|
||||
working_dir: src
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
set -x
|
||||
export CONFIG=$PROJECT_DIRECTORY/.github/reviewers.txt
|
||||
export SCRIPT="$DRIVERS_TOOLS/.evergreen/github_app/assign-reviewer.sh"
|
||||
@ -2067,7 +2033,7 @@ tasks:
|
||||
shell: "bash"
|
||||
working_dir: src
|
||||
script: |
|
||||
${PREPARE_SHELL}
|
||||
. .evergreen/scripts/env.sh
|
||||
set -x
|
||||
export BASE_SHA=${revision}
|
||||
export HEAD_SHA=${github_commit}
|
||||
@ -2120,10 +2086,14 @@ axes:
|
||||
display_name: "RHEL 8.3 (zSeries)"
|
||||
run_on: rhel83-zseries-small
|
||||
batchtime: 10080 # 7 days
|
||||
variables:
|
||||
SKIP_HATCH: true
|
||||
- id: rhel81-power8
|
||||
display_name: "RHEL 8.1 (POWER8)"
|
||||
run_on: rhel81-power8-small
|
||||
batchtime: 10080 # 7 days
|
||||
variables:
|
||||
SKIP_HATCH: true
|
||||
- id: rhel82-arm64
|
||||
display_name: "RHEL 8.2 (ARM64)"
|
||||
run_on: rhel82-arm64-small
|
||||
|
||||
@ -8,7 +8,17 @@ if [ -z "$PYTHON_BINARY" ]; then
|
||||
PYTHON_BINARY=$(find_python3)
|
||||
fi
|
||||
|
||||
if $PYTHON_BINARY -m hatch --version; then
|
||||
# Check if we should skip hatch and run the tests directly.
|
||||
if [ -n "$SKIP_HATCH" ]; then
|
||||
ENV_NAME=testenv-$RANDOM
|
||||
createvirtualenv "$PYTHON_BINARY" $ENV_NAME
|
||||
# shellcheck disable=SC2064
|
||||
trap "deactivate; rm -rf $ENV_NAME" EXIT HUP
|
||||
python -m pip install -e ".[test]"
|
||||
run_hatch() {
|
||||
bash ./.evergreen/run-tests.sh
|
||||
}
|
||||
elif $PYTHON_BINARY -m hatch --version; then
|
||||
run_hatch() {
|
||||
$PYTHON_BINARY -m hatch run "$@"
|
||||
}
|
||||
|
||||
53
.evergreen/scripts/configure-env.sh
Normal file
53
.evergreen/scripts/configure-env.sh
Normal file
@ -0,0 +1,53 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Get the current unique version of this checkout
|
||||
# shellcheck disable=SC2154
|
||||
if [ "$is_patch" = "true" ]; then
|
||||
# shellcheck disable=SC2154
|
||||
CURRENT_VERSION="$(git describe)-patch-$version_id"
|
||||
else
|
||||
CURRENT_VERSION=latest
|
||||
fi
|
||||
|
||||
PROJECT_DIRECTORY="$(pwd)"
|
||||
DRIVERS_TOOLS="$(dirname $PROJECT_DIRECTORY)/drivers-tools"
|
||||
|
||||
# Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory
|
||||
if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
|
||||
DRIVERS_TOOLS=$(cygpath -m $DRIVERS_TOOLS)
|
||||
PROJECT_DIRECTORY=$(cygpath -m $PROJECT_DIRECTORY)
|
||||
fi
|
||||
|
||||
SCRIPT_DIR="$PROJECT_DIRECTORY/.evergreen/scripts"
|
||||
|
||||
if [ -f "$SCRIPT_DIR/env.sh" ]; then
|
||||
echo "Reading $SCRIPT_DIR/env.sh file"
|
||||
. "$SCRIPT_DIR/env.sh"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
export MONGO_ORCHESTRATION_HOME="$DRIVERS_TOOLS/.evergreen/orchestration"
|
||||
export MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin"
|
||||
|
||||
cat <<EOT > $SCRIPT_DIR/env.sh
|
||||
set -o errexit
|
||||
export PROJECT_DIRECTORY="$PROJECT_DIRECTORY"
|
||||
export CURRENT_VERSION="$CURRENT_VERSION"
|
||||
export SKIP_LEGACY_SHELL=1
|
||||
export DRIVERS_TOOLS="$DRIVERS_TOOLS"
|
||||
export MONGO_ORCHESTRATION_HOME="$MONGO_ORCHESTRATION_HOME"
|
||||
export MONGODB_BINARIES="$MONGODB_BINARIES"
|
||||
export PROJECT_DIRECTORY="$PROJECT_DIRECTORY"
|
||||
|
||||
export TMPDIR="$MONGO_ORCHESTRATION_HOME/db"
|
||||
export PATH="$MONGODB_BINARIES:$PATH"
|
||||
# shellcheck disable=SC2154
|
||||
export PROJECT="$project"
|
||||
export PIP_QUIET=1
|
||||
EOT
|
||||
|
||||
# Add these expansions to make it easier to call out tests scripts from the EVG yaml
|
||||
cat <<EOT > expansion.yml
|
||||
DRIVERS_TOOLS: "$DRIVERS_TOOLS"
|
||||
PROJECT_DIRECTORY: "$PROJECT_DIRECTORY"
|
||||
EOT
|
||||
@ -383,10 +383,11 @@ static int millis_from_datetime_ms(PyObject* dt, long long* out){
|
||||
static PyObject* decode_datetime(PyObject* self, long long millis, const codec_options_t* options){
|
||||
PyObject* naive = NULL;
|
||||
PyObject* replace = NULL;
|
||||
PyObject* args = NULL;
|
||||
PyObject* kwargs = NULL;
|
||||
PyObject* value = NULL;
|
||||
struct module_state *state = GETSTATE(self);
|
||||
if (!state) {
|
||||
goto invalid;
|
||||
}
|
||||
if (options->datetime_conversion == DATETIME_MS){
|
||||
return datetime_ms_from_millis(self, millis);
|
||||
}
|
||||
@ -414,8 +415,8 @@ static PyObject* decode_datetime(PyObject* self, long long millis, const codec_o
|
||||
Py_DECREF(utcoffset);
|
||||
return 0;
|
||||
}
|
||||
min_millis_offset = (PyDateTime_DELTA_GET_DAYS(utcoffset) * 86400 +
|
||||
PyDateTime_DELTA_GET_SECONDS(utcoffset)) * 1000 +
|
||||
min_millis_offset = (PyDateTime_DELTA_GET_DAYS(utcoffset) * (int64_t)86400 +
|
||||
PyDateTime_DELTA_GET_SECONDS(utcoffset)) * (int64_t)1000 +
|
||||
(PyDateTime_DELTA_GET_MICROSECONDS(utcoffset) / 1000);
|
||||
}
|
||||
Py_DECREF(utcoffset);
|
||||
@ -433,8 +434,8 @@ static PyObject* decode_datetime(PyObject* self, long long millis, const codec_o
|
||||
Py_DECREF(utcoffset);
|
||||
return 0;
|
||||
}
|
||||
max_millis_offset = (PyDateTime_DELTA_GET_DAYS(utcoffset) * 86400 +
|
||||
PyDateTime_DELTA_GET_SECONDS(utcoffset)) * 1000 +
|
||||
max_millis_offset = (PyDateTime_DELTA_GET_DAYS(utcoffset) * (int64_t)86400 +
|
||||
PyDateTime_DELTA_GET_SECONDS(utcoffset)) * (int64_t)1000 +
|
||||
(PyDateTime_DELTA_GET_MICROSECONDS(utcoffset) / 1000);
|
||||
}
|
||||
Py_DECREF(utcoffset);
|
||||
@ -487,8 +488,6 @@ static PyObject* decode_datetime(PyObject* self, long long millis, const codec_o
|
||||
invalid:
|
||||
Py_XDECREF(naive);
|
||||
Py_XDECREF(replace);
|
||||
Py_XDECREF(args);
|
||||
Py_XDECREF(kwargs);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,9 @@ Changelog
|
||||
Changes in Version 4.9.0
|
||||
-------------------------
|
||||
|
||||
.. warning:: Driver support for MongoDB 3.6 reached end of life in April 2024.
|
||||
PyMongo 4.9 will be the last release to support MongoDB 3.6.
|
||||
|
||||
PyMongo 4.9 brings a number of improvements including:
|
||||
|
||||
- Added support for MongoDB 8.0.
|
||||
@ -11,6 +14,8 @@ PyMongo 4.9 brings a number of improvements including:
|
||||
- Added support for In-Use Encryption range queries with MongoDB 8.0.
|
||||
Added :attr:`~pymongo.encryption.Algorithm.RANGE`.
|
||||
``sparsity`` and ``trim_factor`` are now optional in :class:`~pymongo.encryption_options.RangeOpts`.
|
||||
- Added support for the "delegated" option for the KMIP ``master_key`` in
|
||||
:meth:`~pymongo.encryption.ClientEncryption.create_data_key`.
|
||||
- pymongocrypt>=1.10 is now required for :ref:`In-Use Encryption` support.
|
||||
- Added :meth:`~pymongo.cursor.Cursor.to_list` to :class:`~pymongo.cursor.Cursor`,
|
||||
:class:`~pymongo.command_cursor.CommandCursor`,
|
||||
|
||||
@ -281,6 +281,7 @@ class _AsyncBulk:
|
||||
)
|
||||
if bwc.publish:
|
||||
bwc._succeed(request_id, reply, duration) # type: ignore[arg-type]
|
||||
await client._process_response(reply, bwc.session) # type: ignore[arg-type]
|
||||
except Exception as exc:
|
||||
duration = datetime.datetime.now() - bwc.start_time
|
||||
if isinstance(exc, (NotPrimaryError, OperationFailure)):
|
||||
@ -308,6 +309,9 @@ class _AsyncBulk:
|
||||
|
||||
if bwc.publish:
|
||||
bwc._fail(request_id, failure, duration)
|
||||
# Process the response from the server.
|
||||
if isinstance(exc, (NotPrimaryError, OperationFailure)):
|
||||
await client._process_response(exc.details, bwc.session) # type: ignore[arg-type]
|
||||
raise
|
||||
finally:
|
||||
bwc.start_time = datetime.datetime.now()
|
||||
@ -449,7 +453,6 @@ class _AsyncBulk:
|
||||
else:
|
||||
request_id, msg, to_send = bwc.batch_command(cmd, ops)
|
||||
result = await self.write_command(bwc, cmd, request_id, msg, to_send, client) # type: ignore[arg-type]
|
||||
await client._process_response(result, bwc.session) # type: ignore[arg-type]
|
||||
|
||||
return result, to_send # type: ignore[return-value]
|
||||
|
||||
|
||||
@ -283,6 +283,8 @@ class _AsyncClientBulk:
|
||||
)
|
||||
if bwc.publish:
|
||||
bwc._succeed(request_id, reply, duration) # type: ignore[arg-type]
|
||||
# Process the response from the server.
|
||||
await self.client._process_response(reply, bwc.session) # type: ignore[arg-type]
|
||||
except Exception as exc:
|
||||
duration = datetime.datetime.now() - bwc.start_time
|
||||
if isinstance(exc, (NotPrimaryError, OperationFailure)):
|
||||
@ -312,6 +314,11 @@ class _AsyncClientBulk:
|
||||
bwc._fail(request_id, failure, duration)
|
||||
# Top-level error will be embedded in ClientBulkWriteException.
|
||||
reply = {"error": exc}
|
||||
# Process the response from the server.
|
||||
if isinstance(exc, OperationFailure):
|
||||
await self.client._process_response(exc.details, bwc.session) # type: ignore[arg-type]
|
||||
else:
|
||||
await self.client._process_response({}, bwc.session) # type: ignore[arg-type]
|
||||
finally:
|
||||
bwc.start_time = datetime.datetime.now()
|
||||
return reply # type: ignore[return-value]
|
||||
@ -431,7 +438,6 @@ class _AsyncClientBulk:
|
||||
result = await self.write_command(
|
||||
bwc, cmd, request_id, msg, to_send_ops, to_send_ns, self.client
|
||||
) # type: ignore[arg-type]
|
||||
await self.client._process_response(result, bwc.session) # type: ignore[arg-type]
|
||||
return result, to_send_ops, to_send_ns # type: ignore[return-value]
|
||||
|
||||
async def _process_results_cursor(
|
||||
|
||||
@ -764,6 +764,9 @@ class AsyncClientEncryption(Generic[_DocumentType]):
|
||||
Secret Data managed object.
|
||||
- `endpoint` (string): Optional. Host with optional
|
||||
port, e.g. "example.vault.azure.net:".
|
||||
- `delegated` (bool): Optional. If True (recommended), the
|
||||
KMIP server will perform encryption and decryption. If
|
||||
delegated is not provided, defaults to false.
|
||||
|
||||
:param key_alt_names: An optional list of string alternate
|
||||
names used to reference a key. If a key is created with alternate
|
||||
|
||||
@ -281,6 +281,7 @@ class _Bulk:
|
||||
)
|
||||
if bwc.publish:
|
||||
bwc._succeed(request_id, reply, duration) # type: ignore[arg-type]
|
||||
client._process_response(reply, bwc.session) # type: ignore[arg-type]
|
||||
except Exception as exc:
|
||||
duration = datetime.datetime.now() - bwc.start_time
|
||||
if isinstance(exc, (NotPrimaryError, OperationFailure)):
|
||||
@ -308,6 +309,9 @@ class _Bulk:
|
||||
|
||||
if bwc.publish:
|
||||
bwc._fail(request_id, failure, duration)
|
||||
# Process the response from the server.
|
||||
if isinstance(exc, (NotPrimaryError, OperationFailure)):
|
||||
client._process_response(exc.details, bwc.session) # type: ignore[arg-type]
|
||||
raise
|
||||
finally:
|
||||
bwc.start_time = datetime.datetime.now()
|
||||
@ -449,7 +453,6 @@ class _Bulk:
|
||||
else:
|
||||
request_id, msg, to_send = bwc.batch_command(cmd, ops)
|
||||
result = self.write_command(bwc, cmd, request_id, msg, to_send, client) # type: ignore[arg-type]
|
||||
client._process_response(result, bwc.session) # type: ignore[arg-type]
|
||||
|
||||
return result, to_send # type: ignore[return-value]
|
||||
|
||||
|
||||
@ -283,6 +283,8 @@ class _ClientBulk:
|
||||
)
|
||||
if bwc.publish:
|
||||
bwc._succeed(request_id, reply, duration) # type: ignore[arg-type]
|
||||
# Process the response from the server.
|
||||
self.client._process_response(reply, bwc.session) # type: ignore[arg-type]
|
||||
except Exception as exc:
|
||||
duration = datetime.datetime.now() - bwc.start_time
|
||||
if isinstance(exc, (NotPrimaryError, OperationFailure)):
|
||||
@ -312,6 +314,11 @@ class _ClientBulk:
|
||||
bwc._fail(request_id, failure, duration)
|
||||
# Top-level error will be embedded in ClientBulkWriteException.
|
||||
reply = {"error": exc}
|
||||
# Process the response from the server.
|
||||
if isinstance(exc, OperationFailure):
|
||||
self.client._process_response(exc.details, bwc.session) # type: ignore[arg-type]
|
||||
else:
|
||||
self.client._process_response({}, bwc.session) # type: ignore[arg-type]
|
||||
finally:
|
||||
bwc.start_time = datetime.datetime.now()
|
||||
return reply # type: ignore[return-value]
|
||||
@ -429,7 +436,6 @@ class _ClientBulk:
|
||||
"""Executes a batch of bulkWrite server commands (ack)."""
|
||||
request_id, msg, to_send_ops, to_send_ns = bwc.batch_command(cmd, ops, namespaces)
|
||||
result = self.write_command(bwc, cmd, request_id, msg, to_send_ops, to_send_ns, self.client) # type: ignore[arg-type]
|
||||
self.client._process_response(result, bwc.session) # type: ignore[arg-type]
|
||||
return result, to_send_ops, to_send_ns # type: ignore[return-value]
|
||||
|
||||
def _process_results_cursor(
|
||||
|
||||
@ -762,6 +762,9 @@ class ClientEncryption(Generic[_DocumentType]):
|
||||
Secret Data managed object.
|
||||
- `endpoint` (string): Optional. Host with optional
|
||||
port, e.g. "example.vault.azure.net:".
|
||||
- `delegated` (bool): Optional. If True (recommended), the
|
||||
KMIP server will perform encryption and decryption. If
|
||||
delegated is not provided, defaults to false.
|
||||
|
||||
:param key_alt_names: An optional list of string alternate
|
||||
names used to reference a key. If a key is created with alternate
|
||||
|
||||
255
test/asynchronous/qcheck.py
Normal file
255
test/asynchronous/qcheck.py
Normal file
@ -0,0 +1,255 @@
|
||||
# Copyright 2009-present MongoDB, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from bson.dbref import DBRef
|
||||
from bson.objectid import ObjectId
|
||||
from bson.son import SON
|
||||
|
||||
_IS_SYNC = False
|
||||
|
||||
gen_target = 100
|
||||
reduction_attempts = 10
|
||||
examples = 5
|
||||
|
||||
|
||||
def lift(value):
|
||||
return lambda: value
|
||||
|
||||
|
||||
def choose_lifted(generator_list):
|
||||
return lambda: random.choice(generator_list)
|
||||
|
||||
|
||||
def my_map(generator, function):
|
||||
return lambda: function(generator())
|
||||
|
||||
|
||||
def choose(list):
|
||||
return lambda: random.choice(list)()
|
||||
|
||||
|
||||
def gen_range(start, stop):
|
||||
return lambda: random.randint(start, stop)
|
||||
|
||||
|
||||
def gen_int():
|
||||
max_int = 2147483647
|
||||
return lambda: random.randint(-max_int - 1, max_int)
|
||||
|
||||
|
||||
def gen_float():
|
||||
return lambda: (random.random() - 0.5) * sys.maxsize
|
||||
|
||||
|
||||
def gen_boolean():
|
||||
return lambda: random.choice([True, False])
|
||||
|
||||
|
||||
def gen_printable_char():
|
||||
return lambda: chr(random.randint(32, 126))
|
||||
|
||||
|
||||
def gen_printable_string(gen_length):
|
||||
return lambda: "".join(gen_list(gen_printable_char(), gen_length)())
|
||||
|
||||
|
||||
def gen_char(set=None):
|
||||
return lambda: bytes([random.randint(0, 255)])
|
||||
|
||||
|
||||
def gen_string(gen_length):
|
||||
return lambda: b"".join(gen_list(gen_char(), gen_length)())
|
||||
|
||||
|
||||
def gen_unichar():
|
||||
return lambda: chr(random.randint(1, 0xFFF))
|
||||
|
||||
|
||||
def gen_unicode(gen_length):
|
||||
return lambda: "".join([x for x in gen_list(gen_unichar(), gen_length)() if x not in ".$"])
|
||||
|
||||
|
||||
def gen_list(generator, gen_length):
|
||||
return lambda: [generator() for _ in range(gen_length())]
|
||||
|
||||
|
||||
def gen_datetime():
|
||||
return lambda: datetime.datetime(
|
||||
random.randint(1970, 2037),
|
||||
random.randint(1, 12),
|
||||
random.randint(1, 28),
|
||||
random.randint(0, 23),
|
||||
random.randint(0, 59),
|
||||
random.randint(0, 59),
|
||||
random.randint(0, 999) * 1000,
|
||||
)
|
||||
|
||||
|
||||
def gen_dict(gen_key, gen_value, gen_length):
|
||||
def a_dict(gen_key, gen_value, length):
|
||||
result = {}
|
||||
for _ in range(length):
|
||||
result[gen_key()] = gen_value()
|
||||
return result
|
||||
|
||||
return lambda: a_dict(gen_key, gen_value, gen_length())
|
||||
|
||||
|
||||
def gen_regexp(gen_length):
|
||||
# TODO our patterns only consist of one letter.
|
||||
# this is because of a bug in CPython's regex equality testing,
|
||||
# which I haven't quite tracked down, so I'm just ignoring it...
|
||||
def pattern():
|
||||
return "".join(gen_list(choose_lifted("a"), gen_length)())
|
||||
|
||||
def gen_flags():
|
||||
flags = 0
|
||||
if random.random() > 0.5:
|
||||
flags = flags | re.IGNORECASE
|
||||
if random.random() > 0.5:
|
||||
flags = flags | re.MULTILINE
|
||||
if random.random() > 0.5:
|
||||
flags = flags | re.VERBOSE
|
||||
|
||||
return flags
|
||||
|
||||
return lambda: re.compile(pattern(), gen_flags())
|
||||
|
||||
|
||||
def gen_objectid():
|
||||
return lambda: ObjectId()
|
||||
|
||||
|
||||
def gen_dbref():
|
||||
collection = gen_unicode(gen_range(0, 20))
|
||||
return lambda: DBRef(collection(), gen_mongo_value(1, True)())
|
||||
|
||||
|
||||
def gen_mongo_value(depth, ref):
|
||||
choices = [
|
||||
gen_unicode(gen_range(0, 50)),
|
||||
gen_printable_string(gen_range(0, 50)),
|
||||
my_map(gen_string(gen_range(0, 1000)), bytes),
|
||||
gen_int(),
|
||||
gen_float(),
|
||||
gen_boolean(),
|
||||
gen_datetime(),
|
||||
gen_objectid(),
|
||||
lift(None),
|
||||
]
|
||||
if ref:
|
||||
choices.append(gen_dbref())
|
||||
if depth > 0:
|
||||
choices.append(gen_mongo_list(depth, ref))
|
||||
choices.append(gen_mongo_dict(depth, ref))
|
||||
return choose(choices)
|
||||
|
||||
|
||||
def gen_mongo_list(depth, ref):
|
||||
return gen_list(gen_mongo_value(depth - 1, ref), gen_range(0, 10))
|
||||
|
||||
|
||||
def gen_mongo_dict(depth, ref=True):
|
||||
return my_map(
|
||||
gen_dict(gen_unicode(gen_range(0, 20)), gen_mongo_value(depth - 1, ref), gen_range(0, 10)),
|
||||
SON,
|
||||
)
|
||||
|
||||
|
||||
def simplify(case): # TODO this is a hack
|
||||
if isinstance(case, SON) and "$ref" not in case:
|
||||
simplified = SON(case) # make a copy!
|
||||
if random.choice([True, False]):
|
||||
# delete
|
||||
simplified_keys = list(simplified)
|
||||
if not len(simplified_keys):
|
||||
return (False, case)
|
||||
simplified.pop(random.choice(simplified_keys))
|
||||
return (True, simplified)
|
||||
else:
|
||||
# simplify a value
|
||||
simplified_items = list(simplified.items())
|
||||
if not len(simplified_items):
|
||||
return (False, case)
|
||||
(key, value) = random.choice(simplified_items)
|
||||
(success, value) = simplify(value)
|
||||
simplified[key] = value
|
||||
return (success, success and simplified or case)
|
||||
if isinstance(case, list):
|
||||
simplified = list(case)
|
||||
if random.choice([True, False]):
|
||||
# delete
|
||||
if not len(simplified):
|
||||
return (False, case)
|
||||
simplified.pop(random.randrange(len(simplified)))
|
||||
return (True, simplified)
|
||||
else:
|
||||
# simplify an item
|
||||
if not len(simplified):
|
||||
return (False, case)
|
||||
index = random.randrange(len(simplified))
|
||||
(success, value) = simplify(simplified[index])
|
||||
simplified[index] = value
|
||||
return (success, success and simplified or case)
|
||||
return (False, case)
|
||||
|
||||
|
||||
async def reduce(case, predicate, reductions=0):
|
||||
for _ in range(reduction_attempts):
|
||||
(reduced, simplified) = simplify(case)
|
||||
if reduced and not await predicate(simplified):
|
||||
return await reduce(simplified, predicate, reductions + 1)
|
||||
return (reductions, case)
|
||||
|
||||
|
||||
async def isnt(predicate):
|
||||
async def is_not(x):
|
||||
return not await predicate(x)
|
||||
|
||||
return is_not
|
||||
|
||||
|
||||
async def check(predicate, generator):
|
||||
counter_examples = []
|
||||
for _ in range(gen_target):
|
||||
case = generator()
|
||||
try:
|
||||
if not await predicate(case):
|
||||
reduction = await reduce(case, predicate)
|
||||
counter_examples.append("after {} reductions: {!r}".format(*reduction))
|
||||
except:
|
||||
counter_examples.append(f"{case!r} : {traceback.format_exc()}")
|
||||
return counter_examples
|
||||
|
||||
|
||||
async def check_unittest(test, predicate, generator):
|
||||
counter_examples = await check(predicate, generator)
|
||||
if counter_examples:
|
||||
failures = len(counter_examples)
|
||||
message = "\n".join([" -> %s" % f for f in counter_examples[:examples]])
|
||||
message = "found %d counter examples, displaying first %d:\n%s" % (
|
||||
failures,
|
||||
min(failures, examples),
|
||||
message,
|
||||
)
|
||||
test.fail(message)
|
||||
@ -26,8 +26,8 @@ sys.path[0:0] = [""]
|
||||
from test.asynchronous import AsyncIntegrationTest, async_client_context, remove_all_users, unittest
|
||||
from test.utils import (
|
||||
async_rs_or_single_client_noauth,
|
||||
async_single_client,
|
||||
async_wait_until,
|
||||
single_client,
|
||||
)
|
||||
|
||||
from bson.binary import Binary, UuidRepresentation
|
||||
@ -817,7 +817,7 @@ class AsyncBulkAuthorizationTestBase(AsyncBulkTestBase):
|
||||
roles=[],
|
||||
)
|
||||
|
||||
async_client_context.create_user(self.db.name, "noremove", "pw", ["noremove"])
|
||||
await async_client_context.create_user(self.db.name, "noremove", "pw", ["noremove"])
|
||||
|
||||
async def asyncTearDown(self):
|
||||
await self.db.command("dropRole", "noremove")
|
||||
@ -919,7 +919,7 @@ class AsyncTestBulkAuthorization(AsyncBulkAuthorizationTestBase):
|
||||
username="readonly", password="pw", authSource="pymongo_test"
|
||||
)
|
||||
coll = cli.pymongo_test.test
|
||||
coll.find_one()
|
||||
await coll.find_one()
|
||||
with self.assertRaises(OperationFailure):
|
||||
await coll.bulk_write([InsertOne({"x": 1})])
|
||||
|
||||
@ -930,7 +930,7 @@ class AsyncTestBulkAuthorization(AsyncBulkAuthorizationTestBase):
|
||||
username="noremove", password="pw", authSource="pymongo_test"
|
||||
)
|
||||
coll = cli.pymongo_test.test
|
||||
coll.find_one()
|
||||
await coll.find_one()
|
||||
requests = [
|
||||
InsertOne({"x": 1}),
|
||||
ReplaceOne({"x": 2}, {"x": 2}, upsert=True),
|
||||
@ -954,7 +954,7 @@ class AsyncTestBulkWriteConcern(AsyncBulkTestBase):
|
||||
if cls.w is not None and cls.w > 1:
|
||||
for member in (await async_client_context.hello)["hosts"]:
|
||||
if member != (await async_client_context.hello)["primary"]:
|
||||
cls.secondary = single_client(*partition_node(member))
|
||||
cls.secondary = await async_single_client(*partition_node(member))
|
||||
break
|
||||
|
||||
@classmethod
|
||||
|
||||
@ -617,7 +617,7 @@ class AsyncClientUnitTest(AsyncUnitTest):
|
||||
mock_get_hosts.return_value = [(host, 1)]
|
||||
AsyncMongoClient(host)
|
||||
AsyncMongoClient(multi_host)
|
||||
logs = [record.message for record in cm.records if record.name == "pymongo.client"]
|
||||
logs = [record.getMessage() for record in cm.records if record.name == "pymongo.client"]
|
||||
self.assertEqual(len(logs), 7)
|
||||
|
||||
@patch("pymongo.srv_resolver._SrvResolver.get_hosts")
|
||||
|
||||
@ -21,17 +21,21 @@ import io
|
||||
import sys
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
from test.asynchronous import AsyncIntegrationTest, AsyncUnitTest, async_client_context
|
||||
from test.asynchronous import (
|
||||
AsyncIntegrationTest,
|
||||
AsyncUnitTest,
|
||||
async_client_context,
|
||||
qcheck,
|
||||
unittest,
|
||||
)
|
||||
|
||||
from pymongo.asynchronous.database import AsyncDatabase
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from test import IntegrationTest, qcheck, unittest
|
||||
from test.utils import EventListener, async_rs_or_single_client, rs_or_single_client
|
||||
from test.utils import EventListener, async_rs_or_single_client
|
||||
|
||||
from bson.objectid import ObjectId
|
||||
from gridfs import GridFS
|
||||
from gridfs.asynchronous.grid_file import (
|
||||
_SEEK_CUR,
|
||||
_SEEK_END,
|
||||
@ -44,7 +48,7 @@ from gridfs.asynchronous.grid_file import (
|
||||
from gridfs.errors import NoFile
|
||||
from pymongo import AsyncMongoClient
|
||||
from pymongo.asynchronous.helpers import aiter, anext
|
||||
from pymongo.errors import ConfigurationError, InvalidOperation, ServerSelectionTimeoutError
|
||||
from pymongo.errors import ConfigurationError, ServerSelectionTimeoutError
|
||||
from pymongo.message import _CursorAddress
|
||||
|
||||
_IS_SYNC = False
|
||||
@ -407,8 +411,6 @@ class AsyncTestGridFile(AsyncIntegrationTest):
|
||||
g = AsyncGridOut(self.db.fs, f._id)
|
||||
self.assertEqual(random_string, await g.read())
|
||||
|
||||
# TODO: https://jira.mongodb.org/browse/PYTHON-4708
|
||||
@async_client_context.require_sync
|
||||
async def test_small_chunks(self):
|
||||
self.files = 0
|
||||
self.chunks = 0
|
||||
@ -431,7 +433,7 @@ class AsyncTestGridFile(AsyncIntegrationTest):
|
||||
self.assertEqual(data, await g.read(10) + await g.read(10))
|
||||
return True
|
||||
|
||||
qcheck.check_unittest(self, helper, qcheck.gen_string(qcheck.gen_range(0, 20)))
|
||||
await qcheck.check_unittest(self, helper, qcheck.gen_string(qcheck.gen_range(0, 20)))
|
||||
|
||||
async def test_seek(self):
|
||||
f = AsyncGridIn(self.db.fs, chunkSize=3)
|
||||
|
||||
@ -37,15 +37,15 @@ class TestLogger(AsyncIntegrationTest):
|
||||
with self.assertLogs("pymongo.command", level="DEBUG") as cm:
|
||||
await db.test.insert_many(docs)
|
||||
|
||||
cmd_started_log = json_util.loads(cm.records[0].message)
|
||||
cmd_started_log = json_util.loads(cm.records[0].getMessage())
|
||||
self.assertEqual(len(cmd_started_log["command"]), _DEFAULT_DOCUMENT_LENGTH + 3)
|
||||
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].message)
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].getMessage())
|
||||
self.assertLessEqual(len(cmd_succeeded_log["reply"]), _DEFAULT_DOCUMENT_LENGTH + 3)
|
||||
|
||||
with self.assertLogs("pymongo.command", level="DEBUG") as cm:
|
||||
await db.test.find({}).to_list()
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].message)
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].getMessage())
|
||||
self.assertEqual(len(cmd_succeeded_log["reply"]), _DEFAULT_DOCUMENT_LENGTH + 3)
|
||||
|
||||
async def test_configured_truncation_limit(self):
|
||||
@ -55,14 +55,14 @@ class TestLogger(AsyncIntegrationTest):
|
||||
with self.assertLogs("pymongo.command", level="DEBUG") as cm:
|
||||
await db.command(cmd)
|
||||
|
||||
cmd_started_log = json_util.loads(cm.records[0].message)
|
||||
cmd_started_log = json_util.loads(cm.records[0].getMessage())
|
||||
self.assertEqual(len(cmd_started_log["command"]), 5 + 3)
|
||||
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].message)
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].getMessage())
|
||||
self.assertLessEqual(len(cmd_succeeded_log["reply"]), 5 + 3)
|
||||
with self.assertRaises(OperationFailure):
|
||||
await db.command({"notARealCommand": True})
|
||||
cmd_failed_log = json_util.loads(cm.records[-1].message)
|
||||
cmd_failed_log = json_util.loads(cm.records[-1].getMessage())
|
||||
self.assertEqual(len(cmd_failed_log["failure"]), 5 + 3)
|
||||
|
||||
async def test_truncation_multi_byte_codepoints(self):
|
||||
@ -78,7 +78,7 @@ class TestLogger(AsyncIntegrationTest):
|
||||
with patch.dict("os.environ", {"MONGOB_LOG_MAX_DOCUMENT_LENGTH": length}):
|
||||
with self.assertLogs("pymongo.command", level="DEBUG") as cm:
|
||||
await self.db.test.insert_one({"x": multi_byte_char_str})
|
||||
cmd_started_log = json_util.loads(cm.records[0].message)["command"]
|
||||
cmd_started_log = json_util.loads(cm.records[0].getMessage())["command"]
|
||||
|
||||
cmd_started_log = cmd_started_log[:-3]
|
||||
last_3_bytes = cmd_started_log.encode()[-3:].decode()
|
||||
|
||||
1280
test/asynchronous/test_monitoring.py
Normal file
1280
test/asynchronous/test_monitoring.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,8 @@
|
||||
"replicaset",
|
||||
"sharded",
|
||||
"load-balanced"
|
||||
]
|
||||
],
|
||||
"serverless": "forbid"
|
||||
}
|
||||
],
|
||||
"database_name": "default",
|
||||
|
||||
@ -29,21 +29,22 @@ except ImportError:
|
||||
|
||||
from bson import Timestamp
|
||||
from pymongo import DeleteMany, InsertOne, MongoClient, UpdateOne
|
||||
from pymongo.errors import OperationFailure
|
||||
|
||||
pytestmark = pytest.mark.mockupdb
|
||||
|
||||
|
||||
class TestClusterTime(unittest.TestCase):
|
||||
def cluster_time_conversation(self, callback, replies):
|
||||
def cluster_time_conversation(self, callback, replies, max_wire_version=6):
|
||||
cluster_time = Timestamp(0, 0)
|
||||
server = MockupDB()
|
||||
|
||||
# First test all commands include $clusterTime with wire version 6.
|
||||
# First test all commands include $clusterTime with max_wire_version.
|
||||
_ = server.autoresponds(
|
||||
"ismaster",
|
||||
{
|
||||
"minWireVersion": 0,
|
||||
"maxWireVersion": 6,
|
||||
"maxWireVersion": max_wire_version,
|
||||
"$clusterTime": {"clusterTime": cluster_time},
|
||||
},
|
||||
)
|
||||
@ -166,6 +167,30 @@ class TestClusterTime(unittest.TestCase):
|
||||
request.reply(reply)
|
||||
client.close()
|
||||
|
||||
def test_collection_bulk_error(self):
|
||||
def callback(client: MongoClient[dict]) -> None:
|
||||
with self.assertRaises(OperationFailure):
|
||||
client.db.collection.bulk_write([InsertOne({}), InsertOne({})])
|
||||
|
||||
self.cluster_time_conversation(
|
||||
callback,
|
||||
[{"ok": 0, "errmsg": "mock error"}],
|
||||
)
|
||||
|
||||
def test_client_bulk_error(self):
|
||||
def callback(client: MongoClient[dict]) -> None:
|
||||
with self.assertRaises(OperationFailure):
|
||||
client.bulk_write(
|
||||
[
|
||||
InsertOne({}, namespace="db.collection"),
|
||||
InsertOne({}, namespace="db.collection"),
|
||||
]
|
||||
)
|
||||
|
||||
self.cluster_time_conversation(
|
||||
callback, [{"ok": 0, "errmsg": "mock error"}], max_wire_version=25
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@ -25,6 +25,8 @@ from bson.dbref import DBRef
|
||||
from bson.objectid import ObjectId
|
||||
from bson.son import SON
|
||||
|
||||
_IS_SYNC = True
|
||||
|
||||
gen_target = 100
|
||||
reduction_attempts = 10
|
||||
examples = 5
|
||||
@ -221,7 +223,10 @@ def reduce(case, predicate, reductions=0):
|
||||
|
||||
|
||||
def isnt(predicate):
|
||||
return lambda x: not predicate(x)
|
||||
def is_not(x):
|
||||
return not predicate(x)
|
||||
|
||||
return is_not
|
||||
|
||||
|
||||
def check(predicate, generator):
|
||||
|
||||
@ -611,7 +611,7 @@ class ClientUnitTest(UnitTest):
|
||||
mock_get_hosts.return_value = [(host, 1)]
|
||||
MongoClient(host)
|
||||
MongoClient(multi_host)
|
||||
logs = [record.message for record in cm.records if record.name == "pymongo.client"]
|
||||
logs = [record.getMessage() for record in cm.records if record.name == "pymongo.client"]
|
||||
self.assertEqual(len(logs), 7)
|
||||
|
||||
@patch("pymongo.srv_resolver._SrvResolver.get_hosts")
|
||||
|
||||
@ -21,17 +21,21 @@ import io
|
||||
import sys
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
from test import IntegrationTest, UnitTest, client_context
|
||||
from test import (
|
||||
IntegrationTest,
|
||||
UnitTest,
|
||||
client_context,
|
||||
qcheck,
|
||||
unittest,
|
||||
)
|
||||
|
||||
from pymongo.synchronous.database import Database
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from test import IntegrationTest, qcheck, unittest
|
||||
from test.utils import EventListener, rs_or_single_client
|
||||
|
||||
from bson.objectid import ObjectId
|
||||
from gridfs import GridFS
|
||||
from gridfs.errors import NoFile
|
||||
from gridfs.synchronous.grid_file import (
|
||||
_SEEK_CUR,
|
||||
@ -43,7 +47,7 @@ from gridfs.synchronous.grid_file import (
|
||||
GridOutCursor,
|
||||
)
|
||||
from pymongo import MongoClient
|
||||
from pymongo.errors import ConfigurationError, InvalidOperation, ServerSelectionTimeoutError
|
||||
from pymongo.errors import ConfigurationError, ServerSelectionTimeoutError
|
||||
from pymongo.message import _CursorAddress
|
||||
from pymongo.synchronous.helpers import iter, next
|
||||
|
||||
@ -405,8 +409,6 @@ class TestGridFile(IntegrationTest):
|
||||
g = GridOut(self.db.fs, f._id)
|
||||
self.assertEqual(random_string, g.read())
|
||||
|
||||
# TODO: https://jira.mongodb.org/browse/PYTHON-4708
|
||||
@client_context.require_sync
|
||||
def test_small_chunks(self):
|
||||
self.files = 0
|
||||
self.chunks = 0
|
||||
|
||||
@ -36,15 +36,15 @@ class TestLogger(IntegrationTest):
|
||||
with self.assertLogs("pymongo.command", level="DEBUG") as cm:
|
||||
db.test.insert_many(docs)
|
||||
|
||||
cmd_started_log = json_util.loads(cm.records[0].message)
|
||||
cmd_started_log = json_util.loads(cm.records[0].getMessage())
|
||||
self.assertEqual(len(cmd_started_log["command"]), _DEFAULT_DOCUMENT_LENGTH + 3)
|
||||
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].message)
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].getMessage())
|
||||
self.assertLessEqual(len(cmd_succeeded_log["reply"]), _DEFAULT_DOCUMENT_LENGTH + 3)
|
||||
|
||||
with self.assertLogs("pymongo.command", level="DEBUG") as cm:
|
||||
db.test.find({}).to_list()
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].message)
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].getMessage())
|
||||
self.assertEqual(len(cmd_succeeded_log["reply"]), _DEFAULT_DOCUMENT_LENGTH + 3)
|
||||
|
||||
def test_configured_truncation_limit(self):
|
||||
@ -54,14 +54,14 @@ class TestLogger(IntegrationTest):
|
||||
with self.assertLogs("pymongo.command", level="DEBUG") as cm:
|
||||
db.command(cmd)
|
||||
|
||||
cmd_started_log = json_util.loads(cm.records[0].message)
|
||||
cmd_started_log = json_util.loads(cm.records[0].getMessage())
|
||||
self.assertEqual(len(cmd_started_log["command"]), 5 + 3)
|
||||
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].message)
|
||||
cmd_succeeded_log = json_util.loads(cm.records[1].getMessage())
|
||||
self.assertLessEqual(len(cmd_succeeded_log["reply"]), 5 + 3)
|
||||
with self.assertRaises(OperationFailure):
|
||||
db.command({"notARealCommand": True})
|
||||
cmd_failed_log = json_util.loads(cm.records[-1].message)
|
||||
cmd_failed_log = json_util.loads(cm.records[-1].getMessage())
|
||||
self.assertEqual(len(cmd_failed_log["failure"]), 5 + 3)
|
||||
|
||||
def test_truncation_multi_byte_codepoints(self):
|
||||
@ -77,7 +77,7 @@ class TestLogger(IntegrationTest):
|
||||
with patch.dict("os.environ", {"MONGOB_LOG_MAX_DOCUMENT_LENGTH": length}):
|
||||
with self.assertLogs("pymongo.command", level="DEBUG") as cm:
|
||||
self.db.test.insert_one({"x": multi_byte_char_str})
|
||||
cmd_started_log = json_util.loads(cm.records[0].message)["command"]
|
||||
cmd_started_log = json_util.loads(cm.records[0].getMessage())["command"]
|
||||
|
||||
cmd_started_log = cmd_started_log[:-3]
|
||||
last_3_bytes = cmd_started_log.encode()[-3:].decode()
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import copy
|
||||
import datetime
|
||||
import sys
|
||||
@ -21,8 +22,19 @@ from typing import Any
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from test import IntegrationTest, client_context, client_knobs, sanitize_cmd, unittest
|
||||
from test.utils import EventListener, rs_or_single_client, single_client, wait_until
|
||||
from test import (
|
||||
IntegrationTest,
|
||||
client_context,
|
||||
client_knobs,
|
||||
sanitize_cmd,
|
||||
unittest,
|
||||
)
|
||||
from test.utils import (
|
||||
EventListener,
|
||||
rs_or_single_client,
|
||||
single_client,
|
||||
wait_until,
|
||||
)
|
||||
|
||||
from bson.int64 import Int64
|
||||
from bson.objectid import ObjectId
|
||||
@ -31,23 +43,26 @@ from pymongo import CursorType, DeleteOne, InsertOne, UpdateOne, monitoring
|
||||
from pymongo.errors import AutoReconnect, NotPrimaryError, OperationFailure
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from pymongo.synchronous.command_cursor import CommandCursor
|
||||
from pymongo.synchronous.helpers import next
|
||||
from pymongo.write_concern import WriteConcern
|
||||
|
||||
_IS_SYNC = True
|
||||
|
||||
|
||||
class TestCommandMonitoring(IntegrationTest):
|
||||
listener: EventListener
|
||||
|
||||
@classmethod
|
||||
@client_context.require_connection
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
def _setup_class(cls):
|
||||
super()._setup_class()
|
||||
cls.listener = EventListener()
|
||||
cls.client = rs_or_single_client(event_listeners=[cls.listener], retryWrites=False)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
def _tearDown_class(cls):
|
||||
cls.client.close()
|
||||
super().tearDownClass()
|
||||
super()._tearDown_class()
|
||||
|
||||
def tearDown(self):
|
||||
self.listener.reset()
|
||||
@ -171,7 +186,7 @@ class TestCommandMonitoring(IntegrationTest):
|
||||
self.assertEqual(csr["nextBatch"], [{} for _ in range(4)])
|
||||
finally:
|
||||
# Exhaust the cursor to avoid kill cursors.
|
||||
tuple(cursor)
|
||||
tuple(cursor.to_list())
|
||||
|
||||
def test_find_with_explain(self):
|
||||
cmd = SON([("explain", SON([("find", "test"), ("filter", {})]))])
|
||||
@ -230,7 +245,7 @@ class TestCommandMonitoring(IntegrationTest):
|
||||
self.assertEqual(self.client.address, succeeded.connection_id)
|
||||
finally:
|
||||
# Exhaust the cursor to avoid kill cursors.
|
||||
tuple(cursor)
|
||||
tuple(cursor.to_list())
|
||||
|
||||
def test_find_options(self):
|
||||
query = {
|
||||
@ -356,7 +371,7 @@ class TestCommandMonitoring(IntegrationTest):
|
||||
self.assertEqualReply(expected_result, succeeded.reply)
|
||||
finally:
|
||||
# Exhaust the cursor to avoid kill cursors.
|
||||
tuple(cursor)
|
||||
tuple(cursor.to_list())
|
||||
|
||||
def test_get_more_failure(self):
|
||||
address = self.client.address
|
||||
@ -451,7 +466,7 @@ class TestCommandMonitoring(IntegrationTest):
|
||||
self.assertEqualReply(expected_result, succeeded.reply)
|
||||
|
||||
self.listener.reset()
|
||||
tuple(cursor)
|
||||
tuple(cursor.to_list())
|
||||
self.assertEqual(0, len(self.listener.failed_events))
|
||||
for event in self.listener.started_events:
|
||||
self.assertTrue(isinstance(event, monitoring.CommandStartedEvent))
|
||||
@ -898,7 +913,11 @@ class TestCommandMonitoring(IntegrationTest):
|
||||
self.assertEqual(succeed.operation_id, operation_id)
|
||||
self.assertEqual(1, succeed.reply.get("ok"))
|
||||
self.assertEqual(documents, docs)
|
||||
wait_until(lambda: coll.count_documents({}) == 6, "insert documents with w=0")
|
||||
|
||||
def check():
|
||||
return coll.count_documents({}) == 6
|
||||
|
||||
wait_until(check, "insert documents with w=0")
|
||||
|
||||
def test_bulk_write(self):
|
||||
coll = self.client.pymongo_test.test
|
||||
@ -1058,7 +1077,7 @@ class TestCommandMonitoring(IntegrationTest):
|
||||
# Regardless of server version and use of helpers._first_batch
|
||||
# this test should still pass.
|
||||
self.listener.reset()
|
||||
tuple(self.client.pymongo_test.test.list_indexes())
|
||||
tuple((self.client.pymongo_test.test.list_indexes()).to_list())
|
||||
started = self.listener.started_events[0]
|
||||
succeeded = self.listener.succeeded_events[0]
|
||||
self.assertEqual(0, len(self.listener.failed_events))
|
||||
@ -1119,8 +1138,8 @@ class TestGlobalListener(IntegrationTest):
|
||||
|
||||
@classmethod
|
||||
@client_context.require_connection
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
def _setup_class(cls):
|
||||
super()._setup_class()
|
||||
cls.listener = EventListener()
|
||||
# We plan to call register(), which internally modifies _LISTENERS.
|
||||
cls.saved_listeners = copy.deepcopy(monitoring._LISTENERS)
|
||||
@ -1130,10 +1149,10 @@ class TestGlobalListener(IntegrationTest):
|
||||
cls.client.pymongo_test.command("ping")
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
def _tearDown_class(cls):
|
||||
monitoring._LISTENERS = cls.saved_listeners
|
||||
cls.client.close()
|
||||
super().tearDownClass()
|
||||
super()._tearDown_class()
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
@ -1925,7 +1925,7 @@ class UnifiedSpecTestMixinV1(IntegrationTest):
|
||||
for log in log_list:
|
||||
if log.module == "ocsp_support":
|
||||
continue
|
||||
data = json_util.loads(log.message)
|
||||
data = json_util.loads(log.getMessage())
|
||||
client = data.pop("clientId") if "clientId" in data else data.pop("topologyId")
|
||||
client_to_log[client].append(
|
||||
{
|
||||
|
||||
@ -159,6 +159,7 @@ converted_tests = [
|
||||
"conftest.py",
|
||||
"pymongo_mocks.py",
|
||||
"utils_spec_runner.py",
|
||||
"qcheck.py",
|
||||
"test_bulk.py",
|
||||
"test_client.py",
|
||||
"test_client_bulk_write.py",
|
||||
@ -171,6 +172,7 @@ converted_tests = [
|
||||
"test_session.py",
|
||||
"test_transactions.py",
|
||||
"test_client_context.py",
|
||||
"test_monitoring.py",
|
||||
]
|
||||
|
||||
sync_test_files = [
|
||||
|
||||
Loading…
Reference in New Issue
Block a user