diff --git a/.evergreen/compile-unix.sh b/.evergreen/compile-unix.sh deleted file mode 100755 index 7591c3241..000000000 --- a/.evergreen/compile-unix.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/sh -set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with error if any of the commands fail - -# Supported/used environment variables: -# MARCH Machine Architecture. Defaults to lowercase uname -m -# RELEASE Use the fully qualified release archive - -RELEASE=${RELEASE:-no} - - -# Automatically retrieve the machine architecture, lowercase, unless provided -# as an environment variable (e.g. to force 32bit) -[ -z "$MARCH" ] && MARCH=$(uname -m | tr '[:upper:]' '[:lower:]') - -# Get the kernel name, lowercased -OS=$(uname -s | tr '[:upper:]' '[:lower:]') -echo "OS: $OS" - -# --strip-components is an GNU tar extension. Check if the platform -# (e.g. Solaris) has GNU tar installed as `gtar`, otherwise we assume to be on -# platform that supports it -# command -v returns success error code if found and prints the path to it -if command -v gtar 2>/dev/null; then - TAR=gtar -else - TAR=tar -fi - -# Any architecture specific configuration here -case "$MARCH" in - i386) - CFLAGS="$CFLAGS -m32 -march=i386" - ;; - x86_64) - CFLAGS="$CFLAGS -m64 -march=x86-64" - ;; - ppc64le) - CFLAGS="$CFLAGS -mcpu=power8 -mtune=power8 -mcmodel=medium" - ;; -esac - - -# Operating system specific tweaks -case "$OS" in - darwin) - ;; - - linux) - # Make linux builds a tad faster by parallelise the build - cpus=$(grep -c '^processor' /proc/cpuinfo) - MAKEFLAGS="-j${cpus}" - ;; - - sunos) - # Most normal build tools on the Solaris servers lives here - PATH="/opt/mongodbtoolchain/bin:$PATH" - ;; -esac - -echo "MARCH: $MARCH" -echo "RELEASE: $RELEASE" -echo "OS: $OS" - - -#./configure -make diff --git a/.evergreen/compile-windows.sh b/.evergreen/compile-windows.sh deleted file mode 100755 index 33349bb34..000000000 --- a/.evergreen/compile-windows.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh -set -o igncr # Ignore CR in this script -set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with error if any of the commands fail - -# Supported/used environment variables: -# CC Which compiler to use - - -case "$CC" in - # 64bit specific configuration - *Win64) - ;; - # 32bit specific configuration - *) - ;; -esac - -# Resolve the compiler name to correct MSBuild location -case "$CC" in - "Visual Studio 10 2010") - BUILD="/cygdrive/c/Windows/Microsoft.NET/Framework/v4.0.30319/MSBuild.exe" - ;; - "Visual Studio 10 2010 Win64") - BUILD="/cygdrive/c/Windows/Microsoft.NET/Framework64/v4.0.30319/MSBuild.exe" - ;; - "Visual Studio 12 2013") - BUILD="/cygdrive/c/Program Files (x86)/MSBuild/12.0/Bin/MSBuild.exe" - ;; - "Visual Studio 12 2013 Win64") - BUILD="/cygdrive/c/Program Files (x86)/MSBuild/12.0/Bin/MSBuild.exe" - ;; - "Visual Studio 14 2015") - BUILD="/cygdrive/c/Program Files (x86)/MSBuild/14.0/Bin/MSBuild.exe" - ;; - "Visual Studio 14 2015 Win64") - BUILD="/cygdrive/c/Program Files (x86)/MSBuild/14.0/Bin/MSBuild.exe" - ;; -esac - -export PATH=$PATH:`pwd`/tests:`pwd`/Debug:`pwd`/src/libbson/Debug -CMAKE="/cygdrive/c/cmake/bin/cmake" -INSTALL_DIR="C:/install-dir" - - -"$CMAKE" -G "$CC" "-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}" "-DBSON_ROOT_DIR=${INSTALL_DIR}" $CONFIGURE_FLAGS -"$BUILD" /m ALL_BUILD.vcxproj -"$BUILD" /m INSTALL.vcxproj - diff --git a/.evergreen/compile.sh b/.evergreen/compile.sh deleted file mode 100755 index 8b7820e99..000000000 --- a/.evergreen/compile.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh -set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with error if any of the commands fail - - -DIR=$(dirname $0) - -OS=$(uname -s | tr '[:upper:]' '[:lower:]') -BUILDTOOL=${BUILDTOOL:-autotools} - -case "$OS" in - cygwin*) - sh $DIR/compile-windows.sh - ;; - - *) - # If compiling using multiple different build tools or variants - # that require wildly different scripting, - # this would be a good place to call the different scripts - case "$BUILDTOOL" in - cmake) - sh $DIR/compile-unix-cmake.sh - ;; - autotools) - sh $DIR/compile-unix.sh - ;; - esac - ;; -esac - diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 2755f9966..90b07afec 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -64,7 +64,6 @@ functions: PROJECT_DIRECTORY: "$PROJECT_DIRECTORY" PREPARE_SHELL: | set -o errexit - set -o xtrace export DRIVERS_TOOLS="$DRIVERS_TOOLS" export MONGO_ORCHESTRATION_HOME="$MONGO_ORCHESTRATION_HOME" export MONGODB_BINARIES="$MONGODB_BINARIES" @@ -93,7 +92,7 @@ functions: # If this was a patch build, doing a fresh clone would not actually test the patch cp -R ${PROJECT_DIRECTORY}/ $DRIVERS_TOOLS else - git clone git://github.com/mongodb-labs/drivers-evergreen-tools.git $DRIVERS_TOOLS + git clone https://github.com/mongodb-labs/drivers-evergreen-tools.git --depth 1 $DRIVERS_TOOLS fi echo "{ \"releases\": { \"default\": \"$MONGODB_BINARIES\" }}" > $MONGO_ORCHESTRATION_HOME/orchestration.config @@ -164,7 +163,9 @@ functions: "upload test results": - command: attach.xunit_results params: - file: "${PROJECT_DIRECTORY}/test-results.xml" + # Uploading test results does not work when using ${PROJECT_DIRECTORY}, + # so we use an absolute path to work around this + file: "src/test-results.xml" - command: attach.results params: file_location: "${DRIVERS_TOOLS}/results.json" @@ -174,8 +175,8 @@ functions: params: script: | ${PREPARE_SHELL} - MONGODB_VERSION=${VERSION} ORCHESTRATION_FILE=${ORCHESTRATION_FILE} TOPOLOGY=${TOPOLOGY} AUTH=${AUTH} SSL=${SSL} STORAGE_ENGINE=${STORAGE_ENGINE} sh ${PROJECT_DIRECTORY}/.evergreen/run-orchestration.sh - # run-orchestration generates expansion file with the MONGODB_URI for the cluster + MONGODB_VERSION=${MONGODB_VERSION} ORCHESTRATION_FILE=${ORCHESTRATION_FILE} TOPOLOGY=${TOPOLOGY} AUTH=${AUTH} SSL=${SSL} STORAGE_ENGINE=${STORAGE_ENGINE} LOAD_BALANCER=${LOAD_BALANCER} REQUIRE_API_VERSION=${REQUIRE_API_VERSION} sh ${DRIVERS_TOOLS}/.evergreen/run-orchestration.sh + # run-orchestration generates expansion file with MONGODB_URI and CRYPT_SHARED_LIB_PATH - command: expansions.update params: file: mo-expansion.yml @@ -191,13 +192,87 @@ functions: - command: shell.exec params: script: | - DRIVERS_TOOLS="${DRIVERS_TOOLS}" sh ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/build-mongohouse-local.sh + VARIANT=${VARIANT} DRIVERS_TOOLS="${DRIVERS_TOOLS}" sh ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/build-mongohouse-local.sh - command: shell.exec params: background: true script: | DRIVERS_TOOLS="${DRIVERS_TOOLS}" sh ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/run-mongohouse-local.sh + "create serverless instance": + - command: shell.exec + params: + working_dir: "src" + script: | + ${PREPARE_SHELL} + SERVERLESS_DRIVERS_GROUP=${SERVERLESS_DRIVERS_GROUP} \ + SERVERLESS_API_PUBLIC_KEY=${SERVERLESS_API_PUBLIC_KEY} \ + SERVERLESS_API_PRIVATE_KEY=${SERVERLESS_API_PRIVATE_KEY} \ + bash ${DRIVERS_TOOLS}/.evergreen/serverless/create-instance.sh + - command: expansions.update + params: + file: src/serverless-expansion.yml + - command: shell.exec + params: + shell: bash + script: | + ${PREPARE_SHELL} + + if [ -z "${SERVERLESS_MONGODB_VERSION}" ]; then + echo "expected SERVERLESS_MONGODB_VERSION to be set" + exit 1 + fi + + . ${DRIVERS_TOOLS}/.evergreen/find-python3.sh + PYTHON_BINARY="$(find_python3)" || exit 1 + + # Download the enterprise server download for current platform to $MONGODB_BINARIES. + # This is required for tests that need mongocryptd. + # $MONGODB_BINARIES is added to the $PATH in fetch-source. + ${PYTHON_BINARY} ${DRIVERS_TOOLS}/.evergreen/mongodl.py \ + --component archive \ + --version ${SERVERLESS_MONGODB_VERSION} \ + --edition enterprise \ + --out $MONGODB_BINARIES \ + --strip-path-components 2 + + # Download the crypt_shared dynamic library for the current platform. + ${PYTHON_BINARY} ${DRIVERS_TOOLS}/.evergreen/mongodl.py \ + --component crypt_shared \ + --version ${SERVERLESS_MONGODB_VERSION} \ + --edition enterprise \ + --out . \ + --only "**/mongo_crypt_v1.*" \ + --strip-path-components 1 + + # Find the crypt_shared library file in the current directory and set the CRYPT_SHARED_LIB_PATH to + # the path of that file. Only look for .so, .dll, or .dylib files to prevent matching any other + # downloaded files. + CRYPT_SHARED_LIB_PATH="$(find $(pwd) -maxdepth 1 -type f \ + -name 'mongo_crypt_v1.so' -o \ + -name 'mongo_crypt_v1.dll' -o \ + -name 'mongo_crypt_v1.dylib')" + + echo "CRYPT_SHARED_LIB_PATH: $CRYPT_SHARED_LIB_PATH" >> crypt-expansion.yml + + # Load the expansion file to make an evergreen variable with the current unique version + - command: expansions.update + params: + file: crypt-expansion.yml + + "delete serverless instance": + - command: shell.exec + params: + script: | + # Only run if a serverless instance was started + if [ -n "${SERVERLESS_INSTANCE_NAME}" ]; then + SERVERLESS_INSTANCE_NAME=${SERVERLESS_INSTANCE_NAME} \ + SERVERLESS_DRIVERS_GROUP=${SERVERLESS_DRIVERS_GROUP} \ + SERVERLESS_API_PUBLIC_KEY=${SERVERLESS_API_PUBLIC_KEY} \ + SERVERLESS_API_PRIVATE_KEY=${SERVERLESS_API_PRIVATE_KEY} \ + bash ${DRIVERS_TOOLS}/.evergreen/serverless/delete-instance.sh + fi + "run tests": - command: shell.exec type: test @@ -205,7 +280,34 @@ functions: working_dir: "src" script: | ${PREPARE_SHELL} - PHP_VERSION=${PHP_VERSION} AUTH=${AUTH} SSL=${SSL} MONGODB_URI="${MONGODB_URI}" sh ${PROJECT_DIRECTORY}/.evergreen/run-tests.sh + export AWS_ACCESS_KEY_ID="${client_side_encryption_aws_access_key_id}" + export AWS_SECRET_ACCESS_KEY="${client_side_encryption_aws_secret_access_key}" + export AWS_TEMP_ACCESS_KEY_ID="${client_side_encryption_aws_temp_access_key_id}" + export AWS_TEMP_SECRET_ACCESS_KEY="${client_side_encryption_aws_temp_secret_access_key_key}" + export AWS_TEMP_SESSION_TOKEN="${client_side_encryption_aws_temp_session_token}" + export AZURE_TENANT_ID="${client_side_encryption_azure_tenant_id}" + export AZURE_CLIENT_ID="${client_side_encryption_azure_client_id}" + export AZURE_CLIENT_SECRET="${client_side_encryption_azure_client_secret}" + export GCP_EMAIL="${client_side_encryption_gcp_email}" + export GCP_PRIVATE_KEY="${client_side_encryption_gcp_privatekey}" + export KMIP_ENDPOINT="${client_side_encryption_kmip_endpoint}" + export KMS_ENDPOINT_EXPIRED="${client_side_encryption_kms_endpoint_expired}" + export KMS_ENDPOINT_WRONG_HOST="${client_side_encryption_kms_endpoint_wrong_host}" + export KMS_ENDPOINT_REQUIRE_CLIENT_CERT="${client_side_encryption_kms_endpoint_require_client_cert}" + export KMS_TLS_CA_FILE="${client_side_encryption_kms_tls_ca_file}" + export KMS_TLS_CERTIFICATE_KEY_FILE="${client_side_encryption_kms_tls_certificate_key_file}" + export PATH="${PHP_PATH}/bin:$PATH" + + API_VERSION=${API_VERSION} \ + CRYPT_SHARED_LIB_PATH=${CRYPT_SHARED_LIB_PATH} \ + MONGODB_URI="${MONGODB_URI}" \ + MONGODB_SINGLE_MONGOS_LB_URI="${SINGLE_MONGOS_LB_URI}" \ + MONGODB_MULTI_MONGOS_LB_URI="${MULTI_MONGOS_LB_URI}" \ + PHP_VERSION=${PHP_VERSION} \ + SKIP_CRYPT_SHARED=${SKIP_CRYPT_SHARED} \ + SSL=${SSL} \ + TESTS=${TESTS} \ + sh ${PROJECT_DIRECTORY}/.evergreen/run-tests.sh "run atlas data lake test": - command: shell.exec @@ -214,7 +316,45 @@ functions: working_dir: "src" script: | ${PREPARE_SHELL} - PHP_VERSION=${PHP_VERSION} TESTS="atlas-data-lake" AUTH=${AUTH} SSL=${SSL} MONGODB_URI="${MONGODB_URI}" sh ${PROJECT_DIRECTORY}/.evergreen/run-tests.sh + export PATH="${PHP_PATH}/bin:$PATH" + + MONGODB_URI="mongodb://mhuser:pencil@127.0.0.1:27017" \ + TESTS="atlas-data-lake" \ + sh ${PROJECT_DIRECTORY}/.evergreen/run-tests.sh + + "run serverless tests": + - command: shell.exec + type: test + params: + working_dir: "src" + script: | + ${PREPARE_SHELL} + export AWS_ACCESS_KEY_ID="${client_side_encryption_aws_access_key_id}" + export AWS_SECRET_ACCESS_KEY="${client_side_encryption_aws_secret_access_key}" + export AWS_TEMP_ACCESS_KEY_ID="${client_side_encryption_aws_temp_access_key_id}" + export AWS_TEMP_SECRET_ACCESS_KEY="${client_side_encryption_aws_temp_secret_access_key_key}" + export AWS_TEMP_SESSION_TOKEN="${client_side_encryption_aws_temp_session_token}" + export AZURE_TENANT_ID="${client_side_encryption_azure_tenant_id}" + export AZURE_CLIENT_ID="${client_side_encryption_azure_client_id}" + export AZURE_CLIENT_SECRET="${client_side_encryption_azure_client_secret}" + export GCP_EMAIL="${client_side_encryption_gcp_email}" + export GCP_PRIVATE_KEY="${client_side_encryption_gcp_privatekey}" + export KMIP_ENDPOINT="${client_side_encryption_kmip_endpoint}" + export KMS_ENDPOINT_EXPIRED="${client_side_encryption_kms_endpoint_expired}" + export KMS_ENDPOINT_WRONG_HOST="${client_side_encryption_kms_endpoint_wrong_host}" + export KMS_ENDPOINT_REQUIRE_CLIENT_CERT="${client_side_encryption_kms_endpoint_require_client_cert}" + export KMS_TLS_CA_FILE="${client_side_encryption_kms_tls_ca_file}" + export KMS_TLS_CERTIFICATE_KEY_FILE="${client_side_encryption_kms_tls_certificate_key_file}" + export MONGODB_IS_SERVERLESS=on + export MONGODB_USERNAME=${SERVERLESS_ATLAS_USER} + export MONGODB_PASSWORD=${SERVERLESS_ATLAS_PASSWORD} + export PATH="${PHP_PATH}/bin:$PATH" + + CRYPT_SHARED_LIB_PATH=${CRYPT_SHARED_LIB_PATH} \ + MONGODB_URI="${SERVERLESS_URI}" \ + SKIP_CRYPT_SHARED=${SKIP_CRYPT_SHARED} \ + TESTS="serverless" \ + sh ${PROJECT_DIRECTORY}/.evergreen/run-tests.sh "cleanup": - command: shell.exec @@ -261,7 +401,97 @@ functions: ${PREPARE_SHELL} file="${PROJECT_DIRECTORY}/.evergreen/install-dependencies.sh" # Don't use ${file} syntax here because evergreen treats it as an empty expansion. - [ -f "$file" ] && PHP_VERSION=${PHP_VERSION} DRIVER_VERSION=${DRIVER_VERSION} DRIVER_REPO=${DRIVER_REPO} DRIVER_BRANCH=${DRIVER_BRANCH} DEPENDENCIES=${DEPENDENCIES} sh $file || echo "$file not available, skipping" + [ -f "$file" ] && PHP_VERSION=${PHP_VERSION} EXTENSION_VERSION=${EXTENSION_VERSION} EXTENSION_REPO=${EXTENSION_REPO} EXTENSION_BRANCH=${EXTENSION_BRANCH} DEPENDENCIES=${DEPENDENCIES} sh $file || echo "$file not available, skipping" + # install-dependencies generates expansion file with the PHP_PATH for the chosen PHP version + - command: expansions.update + params: + file: src/php-expansion.yml + + "start load balancer": + - command: shell.exec + params: + script: | + MONGODB_URI="${MONGODB_URI}" ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh start + - command: expansions.update + params: + file: lb-expansion.yml + + "stop load balancer": + - command: shell.exec + params: + script: | + # Only run if a load balancer was started + if [ -n "${SINGLE_MONGOS_LB_URI}" ]; then + ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh stop + fi + + "start kms servers": + - command: shell.exec + # Init venv without background:true to install dependencies + params: + shell: bash + script: |- + set -o errexit + cd ${DRIVERS_TOOLS}/.evergreen/csfle + . ./activate-kmstlsvenv.sh + - command: shell.exec + params: + background: true + shell: bash + # Use different ports for KMS HTTP servers to avoid conflicts with load balancers + script: |- + set -o errexit + cd ${DRIVERS_TOOLS}/.evergreen/csfle + . ./activate-kmstlsvenv.sh + python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8100 & + python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 8101 & + python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 8102 --require_client_cert & + python -u kms_kmip_server.py --port 5698 & + - command: expansions.update + params: + updates: + - key: client_side_encryption_kms_tls_ca_file + value: ${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem + - key: client_side_encryption_kms_tls_certificate_key_file + value: ${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem + - key: client_side_encryption_kms_endpoint_expired + value: 127.0.0.1:8100 + - key: client_side_encryption_kms_endpoint_wrong_host + value: 127.0.0.1:8101 + - key: client_side_encryption_kms_endpoint_require_client_cert + value: 127.0.0.1:8102 + - key: client_side_encryption_kmip_endpoint + value: localhost:5698 + + "set aws temp creds": + - command: shell.exec + params: + shell: bash + script: |- + set -o errexit + + export AWS_ACCESS_KEY_ID="${client_side_encryption_aws_access_key_id}" + export AWS_SECRET_ACCESS_KEY="${client_side_encryption_aws_secret_access_key}" + export AWS_DEFAULT_REGION="us-east-1" + + pushd ${DRIVERS_TOOLS}/.evergreen/csfle + . ./activate-kmstlsvenv.sh + . ./set-temp-creds.sh + popd + + if [ -z "$CSFLE_AWS_TEMP_ACCESS_KEY_ID" ]; then + echo "Failed to set AWS temporary credentials!" + exit 1 + fi + + cat < aws-expansion.yml + client_side_encryption_aws_temp_access_key_id: "$CSFLE_AWS_TEMP_ACCESS_KEY_ID" + client_side_encryption_aws_temp_secret_access_key_key: "$CSFLE_AWS_TEMP_SECRET_ACCESS_KEY" + client_side_encryption_aws_temp_session_token: "$CSFLE_AWS_TEMP_SESSION_TOKEN" + EOT + - command: expansions.update + params: + file: aws-expansion.yml pre: - func: "fetch source" @@ -276,6 +506,8 @@ post: # - func: "upload working dir" - func: "upload mo artifacts" - func: "upload test results" + - func: "delete serverless instance" + - func: "stop load balancer" - func: "stop mongo-orchestration" - func: "cleanup" @@ -289,7 +521,6 @@ tasks: type: test params: script: | - set -o xtrace . ${DRIVERS_TOOLS}/.evergreen/download-mongodb.sh || true get_distro || true echo $DISTRO @@ -319,6 +550,8 @@ tasks: - func: "bootstrap mongo-orchestration" vars: TOPOLOGY: "server" + - func: "start kms servers" + - func: "set aws temp creds" - func: "run tests" - name: "test-replica_set" @@ -327,6 +560,8 @@ tasks: - func: "bootstrap mongo-orchestration" vars: TOPOLOGY: "replica_set" + - func: "start kms servers" + - func: "set aws temp creds" - func: "run tests" - name: "test-sharded_cluster" @@ -335,6 +570,8 @@ tasks: - func: "bootstrap mongo-orchestration" vars: TOPOLOGY: "sharded_cluster" + - func: "start kms servers" + - func: "set aws temp creds" - func: "run tests" - name: "test-atlas-data-lake" @@ -342,136 +579,220 @@ tasks: - func: "bootstrap mongohoused" - func: "run atlas data lake test" + - name: "test-requireApiVersion" + tags: ["versioned-api"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + AUTH: "auth" + REQUIRE_API_VERSION: "yes" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + API_VERSION: "1" + + - name: "test-acceptApiVersion2" + tags: ["versioned-api"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + ORCHESTRATION_FILE: "versioned-api-testing.json" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "versioned-api" + + - name: "test-serverless" + tags: ["serverless"] + commands: + - func: "create serverless instance" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run serverless tests" + + - name: "test-loadBalanced" + tags: ["loadbalanced"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + LOAD_BALANCER: "true" + SSL: "yes" + - func: "start load balancer" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + # Note: loadBalanced=true should already be appended to SINGLE_MONGOS_LB_URI + MONGODB_URI: "${SINGLE_MONGOS_LB_URI}" + SSL: "yes" + # Note: "stop load balancer" will be called from "post" + + - name: "test-skip_crypt_shared" + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + SKIP_CRYPT_SHARED: "yes" + TESTS: "csfle" + + - name: "test-without_aws_creds" + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + - func: "start kms servers" + - func: "run tests" + vars: + client_side_encryption_aws_access_key_id: "" + client_side_encryption_aws_secret_access_key: "" + TESTS: "csfle-without-aws-creds" # }}} axes: + # Note: install-dependencies.sh will search for the latest minor version + # matching the PHP_VERSION constant - id: php-versions display_name: PHP Version values: + - id: "8.2" + display_name: "PHP 8.2" + variables: + PHP_VERSION: "8.2" + - id: "8.1" + display_name: "PHP 8.1" + variables: + PHP_VERSION: "8.1" + - id: "8.0" + display_name: "PHP 8.0" + variables: + PHP_VERSION: "8.0" - id: "7.4" - display_name: "7.4" + display_name: "PHP 7.4" variables: - PHP_VERSION: "7.4.7" + PHP_VERSION: "7.4" - id: "7.3" - display_name: "7.3" + display_name: "PHP 7.3" variables: - PHP_VERSION: "7.3.8" + PHP_VERSION: "7.3" - id: "7.2" - display_name: "7.2" - variables: - PHP_VERSION: "7.2.21" - - id: "7.1" - display_name: "7.1" - variables: - PHP_VERSION: "7.1.31" - - id: "7.0" - display_name: "7.0" + display_name: "PHP 7.2" variables: - PHP_VERSION: "7.0.32" + PHP_VERSION: "7.2" - id: php-edge-versions display_name: PHP Version values: - id: "latest-stable" - display_name: "7.4" + display_name: "PHP 8.2" variables: - PHP_VERSION: "7.4.7" + PHP_VERSION: "8.2" - id: "oldest-supported" - display_name: "7.0" + display_name: "PHP 7.2" variables: - PHP_VERSION: "7.0.32" + PHP_VERSION: "7.2" - - id: versions + - id: mongodb-versions display_name: MongoDB Version values: - id: "latest" - display_name: "latest" + display_name: "MongoDB latest" + variables: + MONGODB_VERSION: "latest" + - id: "rapid" + display_name: "MongoDB rapid" + variables: + MONGODB_VERSION: "rapid" + - id: "7.0" + display_name: "MongoDB 7.0" + variables: + MONGODB_VERSION: "7.0" + - id: "6.0" + display_name: "MongoDB 6.0" variables: - VERSION: "latest" + MONGODB_VERSION: "6.0" + - id: "5.0" + display_name: "MongoDB 5.0" + variables: + MONGODB_VERSION: "5.0" - id: "4.4" - display_name: "4.4" + display_name: "MongoDB 4.4" variables: - VERSION: "4.4" + MONGODB_VERSION: "4.4" - id: "4.2" - display_name: "4.2" + display_name: "MongoDB 4.2" variables: - VERSION: "4.2" + MONGODB_VERSION: "4.2" - id: "4.0" - display_name: "4.0" + display_name: "MongoDB 4.0" variables: - VERSION: "4.0" + MONGODB_VERSION: "4.0" - id: "3.6" - display_name: "3.6" - variables: - VERSION: "3.6" - - id: "3.4" - display_name: "3.4" - variables: - VERSION: "3.4" - - id: "3.2" - display_name: "3.2" - variables: - VERSION: "3.2" - - id: "3.0" - display_name: "3.0" + display_name: "MongoDB 3.6" variables: - VERSION: "3.0" + MONGODB_VERSION: "3.6" - - id: edge-versions + - id: mongodb-edge-versions display_name: MongoDB Version values: - id: "latest-stable" - display_name: "4.4" + display_name: "MongoDB 6.0" variables: - VERSION: "4.4" + MONGODB_VERSION: "6.0" - id: "oldest-supported" - display_name: "3.0" + display_name: "MongoDB 3.6" variables: - VERSION: "3.0" + MONGODB_VERSION: "3.6" - id: driver-versions display_name: Driver Version values: - - id: "lowest-supported" - display_name: "1.8.1" + - id: "oldest-supported" + display_name: "PHPC 1.16.0" variables: - DRIVER_VERSION: "1.8.1" + EXTENSION_VERSION: "1.16.0" - id: "latest-stable" - display_name: "1.9-stable" - variables: - DRIVER_VERSION: "stable" - - id: "1.8-dev" - display_name: "1.8-dev" + display_name: "PHPC (1.16.x)" variables: - DRIVER_BRANCH: "v1.8" - - id: "1.9-dev" - display_name: "1.9-dev" - variables: - DRIVER_BRANCH: "v1.9" + EXTENSION_VERSION: "stable" - id: "latest-dev" - display_name: "1.10-dev (master)" + display_name: "PHPC (1.17-dev)" variables: - DRIVER_BRANCH: "master" + EXTENSION_BRANCH: "master" - - id: os-php7 + - id: os display_name: OS values: - - id: debian92-test - display_name: "Debian 9.2" - run_on: debian92-test - - id: rhel70-test - display_name: "RHEL 7.0" + - id: debian11 + display_name: "Debian 11" + run_on: debian11 + - id: debian10 + display_name: "Debian 10" + run_on: debian10 + - id: debian92 + display_name: "Debian 9.2" + run_on: debian92 + - id: rhel70 + display_name: "RHEL 7.0" run_on: rhel70 - id: rhel71-power8 - display_name: "RHEL 7.1 Power 8" - run_on: rhel71-power8-test - - id: rhel74-zseries - display_name: "RHEL 7.4 zSeries" - run_on: rhel74-zseries-test - - id: ubuntu1804-arm64-test - display_name: "Ubuntu 18.04 ARM64" + display_name: "RHEL 7.1 Power 8" + run_on: rhel71-power8-build + - id: rhel72-zseries + display_name: "RHEL 7.2 zSeries" + run_on: rhel72-zseries-build + - id: ubuntu1804-arm64 + display_name: "Ubuntu 18.04 ARM64" run_on: ubuntu1804-arm64-test - id: topology @@ -539,56 +860,89 @@ axes: DEPENDENCIES: "lowest" buildvariants: - -# Tests all PHP versions on all operating systems. -# Only tests against latest MongoDB and ext-mongodb versions +# Test all PHP versions with latest-stable MongoDB and PHPC on Debian - matrix_name: "test-php-versions" - matrix_spec: {"os-php7": "*", "php-versions": "*", "edge-versions": "latest-stable", "driver-versions": "latest-stable" } + matrix_spec: { "os": "debian11", "mongodb-edge-versions": "latest-stable", "php-versions": "*", "driver-versions": "latest-stable" } + display_name: "${os}, ${mongodb-edge-versions}, ${php-versions}, ${driver-versions}" exclude_spec: - # rhel71-power8 fails due to not reaching pecl - - { "os-php7": "rhel71-power8", "php-versions": "*", edge-versions: "*", "driver-versions": "*" } - # rhel74-zseries doesn't start in a timely fashion - most likely missing executors - - { "os-php7": "rhel74-zseries", "php-versions": "*", edge-versions: "*", "driver-versions": "*" } - display_name: "* ${os-php7}, PHP ${php-versions}, MongoDB ${edge-versions}, ext-mongodb ${driver-versions}" + # Exclude "latest-stable" PHP version for Debian 11 (see: test-mongodb-versions matrix) + - { "os": "debian11", "mongodb-edge-versions": "latest-stable", "php-versions": "8.2", "driver-versions": "latest-stable" } tasks: - name: "test-standalone" - name: "test-replica_set" - name: "test-sharded_cluster" -# Tests all driver versions on all PHP versions -# Only tests on Ubuntu 18.04 and latest MongoDB -- matrix_name: "test-driver-versions" - matrix_spec: {"os-php7": "ubuntu1804-arm64-test", "php-versions": "*", "edge-versions": "latest-stable", "driver-versions": "*" } - display_name: "ext-mongodb ${driver-versions}, PHP ${php-versions}, ${os-php7}, MongoDB ${edge-versions}" - tasks: - - name: "test-standalone" - - name: "test-replica_set" - - name: "test-sharded_cluster" - -# Tests all MongoDB versions -# Only tests on Ubuntu 18.04, with latest stable PHP and driver versions -# Tests against various topologies +# Test all topologies and MongoDB versions with latest-stable PHP and PHPC on Debian - matrix_name: "test-mongodb-versions" - matrix_spec: {"os-php7": "rhel70-test", "php-edge-versions": "latest-stable", "versions": "*", "driver-versions": "latest-stable" } - display_name: "MongoDB ${versions}, PHP ${php-edge-versions}, ${os-php7}, ext-mongodb ${driver-versions}" + matrix_spec: { "os": ["debian92", "debian11"], "mongodb-versions": "*", "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + display_name: "${os}, ${mongodb-versions}, ${php-edge-versions}, ${driver-versions}" + exclude_spec: + # Debian 9.2 only supports up to MongoDB 5.0 + - { "os": "debian92", "mongodb-versions": ["6.0", "7.0", "rapid", "latest"], "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + - { "os": "debian11", "mongodb-versions": ["3.6", "4.0", "4.2", "4.4", "5.0"], "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } tasks: - name: "test-standalone" - name: "test-replica_set" - name: "test-sharded_cluster" -# Tests oldest supported version -# Enables --prefer-lowest for composer to test oldest dependencies against all server versions -- matrix_name: "test-dependencies" - matrix_spec: { "dependencies": "lowest", "os-php7": "rhel70-test", "php-edge-versions": "oldest-supported", "versions": "*", "driver-versions": "lowest-supported" } - display_name: "Dependencies: ${dependencies}, MongoDB ${versions}, PHP ${php-edge-versions}, ${os-php7}, ext-mongodb ${driver-versions}" +# Test oldest-supported PHP, MongoDB, and driver versions with lowest dependencies on Debian +- matrix_name: "test-oldest-supported" + matrix_spec: { "os": "debian92", "mongodb-edge-versions": "oldest-supported", "php-edge-versions": "oldest-supported", "driver-versions": "oldest-supported", "dependencies": "lowest" } + display_name: "Lowest Dependencies: ${os}, ${mongodb-edge-versions}, ${php-edge-versions}, ${driver-versions}" tasks: - name: "test-standalone" - name: "test-replica_set" - name: "test-sharded_cluster" - matrix_name: "atlas-data-lake-test" - matrix_spec: { "php-edge-versions": "latest-stable" } - display_name: "Atlas Data Lake test" - run_on: rhel70 + matrix_spec: { "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + display_name: "Atlas Data Lake" + run_on: debian11 + expansions: + VARIANT: debian11 # Referenced by ADL build script for downloading MQLRun tasks: - name: "test-atlas-data-lake" + +- matrix_name: "test-requireApiVersion" + matrix_spec: { "os": "debian11", "mongodb-versions": "*", "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + display_name: "Versioned API - ${mongodb-versions}" + exclude_spec: + # Stable API is available from MongoDB 5.0+ + - { "os": "debian11", "mongodb-versions": ["3.6", "4.0", "4.2", "4.4"], "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + tasks: + - .versioned-api + +- matrix_name: "serverless" + matrix_spec: { "os": "debian11", "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + display_name: "Serverless" + tasks: + - .serverless + +- matrix_name: "test-loadBalanced" + matrix_spec: { "os": "debian11", "mongodb-versions": "*", "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + display_name: "Load balanced - ${mongodb-versions}" + exclude_spec: + # Load balancer is available from MongoDB 5.0+ + - { "os": "debian11", "mongodb-versions": ["3.6", "4.0", "4.2", "4.4"], "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + tasks: + - name: "test-loadBalanced" + +- matrix_name: "test-csfle-skip_crypt_shared" + matrix_spec: { "os": "debian11", "mongodb-versions": "*", "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + display_name: "CSFLE skip_crypt_shared - ${mongodb-versions}" + exclude_spec: + # CSFLE crypt_shared is available from MongoDB 6.0+ + - { "os": "debian11", "mongodb-versions": ["3.6", "4.0", "4.2", "4.4", "5.0"], "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + tasks: + - name: "test-skip_crypt_shared" + +# Run CSFLE tests without AWS credentials (for "On-demand AWS Credentials" prose test) +- matrix_name: "test-csfle-without_aws_creds" + matrix_spec: { "os": ["debian92", "debian11"], "mongodb-versions": "*", "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + display_name: "CSFLE without_aws_creds - ${mongodb-versions}" + exclude_spec: + # CSFLE is available from MongoDB 4.2+ and Debian 9.2 only supports up to MongoDB 5.0 + - { "os": "debian92", "mongodb-versions": ["3.6", "4.0", "6.0", "rapid", "latest"], "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + - { "os": "debian11", "mongodb-versions": ["3.6", "4.0", "4.2", "4.4", "5.0"], "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } + tasks: + - name: "test-without_aws_creds" diff --git a/.evergreen/download-mongodb.sh b/.evergreen/download-mongodb.sh deleted file mode 100755 index 26238667d..000000000 --- a/.evergreen/download-mongodb.sh +++ /dev/null @@ -1,391 +0,0 @@ -#!/bin/sh - -#For future use the feed to get full list of distros : http://downloads.mongodb.org/full.json - -set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with error if any of the commands fail - -get_distro () -{ - if [ -f /etc/os-release ]; then - . /etc/os-release - DISTRO="${ID}-${VERSION_ID}" - elif command -v lsb_release >/dev/null 2>&1; then - name=$(lsb_release -s -i) - if [ "$name" = "RedHatEnterpriseServer" ]; then # RHEL 6.2 at least - name="rhel" - fi - version=$(lsb_release -s -r) - DISTRO="${name}-${version}" - elif [ -f /etc/redhat-release ]; then - release=$(cat /etc/redhat-release) - - if [[ "$release" =~ "Red Hat" ]]; then - name="rhel" - elif [[ "$release" =~ "Fedora" ]]; then - name="fedora" - fi - version=$(echo $release | sed 's/.*\([[:digit:]]\).*/\1/g') - DISTRO="${name}-${version}" - elif [ -f /etc/lsb-release ]; then - . /etc/lsb-release - DISTRO="${DISTRIB_ID}-${DISTRIB_RELEASE}" - elif grep -R "Amazon Linux" "/etc/system-release" >/dev/null 2>&1; then - DISTRO="amzn64" - fi - - OS_NAME=$(uname -s) - MARCH=$(uname -m) - DISTRO=$(echo "$OS_NAME-$DISTRO-$MARCH" | tr '[:upper:]' '[:lower:]') - - echo $DISTRO -} - -# get_mongodb_download_url_for "linux-distro-version-architecture" "latest|40|36|34|32|30|28|26|24" -# Sets EXTRACT to aproprate extract command -# Sets MONGODB_DOWNLOAD_URL to the aproprate download url -get_mongodb_download_url_for () -{ - _DISTRO=$1 - _VERSION=$2 - - VERSION_44="v4.4-latest" - VERSION_42="4.2.5" - VERSION_40="4.0.17" - VERSION_36="3.6.17" - VERSION_34="3.4.24" - VERSION_32="3.2.22" - VERSION_30="3.0.15" - VERSION_26="2.6.12" - VERSION_24="2.4.14" - - EXTRACT="tar zxf" - # getdata matrix on: - # https://evergreen.mongodb.com/version/5797f0493ff12235e5001f05 - case "$_DISTRO" in - darwin*) - MONGODB_LATEST="http://downloads.10gen.com/osx/mongodb-macos-x86_64-enterprise-latest.tgz" - MONGODB_44="http://downloads.10gen.com/osx/mongodb-macos-x86_64-enterprise-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/osx/mongodb-macos-x86_64-enterprise-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/osx/mongodb-osx-x86_64-enterprise-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/osx/mongodb-osx-x86_64-enterprise-${VERSION_36}.tgz" - MONGODB_34="http://downloads.10gen.com/osx/mongodb-osx-x86_64-enterprise-${VERSION_34}.tgz" - MONGODB_32="http://downloads.10gen.com/osx/mongodb-osx-x86_64-enterprise-${VERSION_32}.tgz" - MONGODB_30="https://fastdl.mongodb.org/osx/mongodb-osx-x86_64-${VERSION_30}.tgz" - MONGODB_26="https://fastdl.mongodb.org/osx/mongodb-osx-x86_64-${VERSION_26}.tgz" - MONGODB_24="https://fastdl.mongodb.org/osx/mongodb-osx-x86_64-${VERSION_24}.tgz" - ;; - sunos*i86pc) - MONGODB_LATEST="https://fastdl.mongodb.org/sunos5/mongodb-sunos5-x86_64-latest.tgz" - MONGODB_34="https://fastdl.mongodb.org/sunos5/mongodb-sunos5-x86_64-3.4.5.tgz" - MONGODB_32="https://fastdl.mongodb.org/sunos5/mongodb-sunos5-x86_64-3.2.14.tgz" - MONGODB_30="https://fastdl.mongodb.org/sunos5/mongodb-sunos5-x86_64-${VERSION_30}.tgz" - MONGODB_26="https://fastdl.mongodb.org/sunos5/mongodb-sunos5-x86_64-${VERSION_26}.tgz" - MONGODB_24="https://fastdl.mongodb.org/sunos5/mongodb-sunos5-x86_64-${VERSION_24}.tgz" - ;; - linux-rhel-8*) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel80-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel80-${VERSION_44}.tgz" - ;; - linux-rhel-7*-s390x) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel72-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel72-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel72-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel72-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel72-3.6.4.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel72-3.4.14.tgz" - ;; - linux-rhel-7.1-ppc64le) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-rhel71-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-rhel71-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-rhel71-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-rhel71-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-rhel71-${VERSION_36}.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-rhel71-${VERSION_34}.tgz" - MONGODB_32="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-rhel71-${VERSION_32}.tgz" - ;; - linux-rhel-7.0*) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel70-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel70-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel70-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel70-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel70-${VERSION_36}.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel70-${VERSION_34}.tgz" - MONGODB_32="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel70-${VERSION_32}.tgz" - MONGODB_30="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel70-${VERSION_30}.tgz" - MONGODB_26="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel70-${VERSION_26}.tgz" - ;; - linux-rhel-6*-s390x) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel67-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel67-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel67-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel67-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel67-${VERSION_36}.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-rhel67-${VERSION_34}.tgz" - ;; - linux-rhel-6.2*) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel62-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel62-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel62-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel62-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel62-${VERSION_36}.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel62-${VERSION_34}.tgz" - MONGODB_32="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel62-${VERSION_32}.tgz" - MONGODB_30="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel62-${VERSION_30}.tgz" - MONGODB_26="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-rhel62-${VERSION_26}.tgz" - MONGODB_24="http://downloads.10gen.com/linux/mongodb-linux-x86_64-subscription-rhel62-${VERSION_24}.tgz" - ;; - linux-rhel-5.5*) - MONGODB_LATEST="http://downloads.mongodb.org/linux/mongodb-linux-x86_64-rhel55-latest.tgz" - MONGODB_32="http://downloads.mongodb.org/linux/mongodb-linux-x86_64-rhel55-${VERSION_32}.tgz" - MONGODB_30="http://downloads.mongodb.org/linux/mongodb-linux-x86_64-rhel55-${VERSION_30}.tgz" - ;; - linux-sles-11*-x86_64) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse11-latest.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse11-${VERSION_36}.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse11-${VERSION_34}.tgz" - MONGODB_32="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse11-${VERSION_32}.tgz" - MONGODB_30="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse11-${VERSION_30}.tgz" - MONGODB_26="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse11-${VERSION_26}.tgz" - MONGODB_24="http://downloads.10gen.com/linux/mongodb-linux-x86_64-subscription-suse11-${VERSION_24}.tgz" - ;; - linux-sles-12*-s390x) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-suse12-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-suse12-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-suse12-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-suse12-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-suse12-3.6.3.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-suse12-3.4.13.tgz" - ;; - linux-sles-12*-x86_64) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse12-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse12-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse12-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse12-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse12-${VERSION_36}.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse12-${VERSION_34}.tgz" - MONGODB_32="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-suse12-${VERSION_32}.tgz" - ;; - linux-amzn-2018*-x86_64) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amzn64-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amzn64-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amzn64-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amzn64-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amzn64-${VERSION_36}.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amzn64-${VERSION_34}.tgz" - MONGODB_32="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amzn64-${VERSION_32}.tgz" - MONGODB_30="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amzn64-${VERSION_30}.tgz" - MONGODB_26="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amzn64-${VERSION_26}.tgz" - MONGODB_24="http://downloads.10gen.com/linux/mongodb-linux-x86_64-subscription-amzn64-${VERSION_24}.tgz" - ;; - linux-amzn-2-x86_64) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amazon2-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amazon2-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amazon2-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-amazon2-${VERSION_40}.tgz" - ;; - linux-debian-7*) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian71-latest.tgz" - # SERVER-32999 removed support for Debian 7. - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian71-3.6.5.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian71-3.4.15.tgz" - MONGODB_32="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian71-3.2.20.tgz" - MONGODB_30="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian71-${VERSION_30}.tgz" - MONGODB_26="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian71-${VERSION_26}.tgz" - ;; - linux-debian-8*) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian81-latest.tgz" - # SERVER-37767 Removed support for Debian 8 - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian81-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian81-${VERSION_36}.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian81-${VERSION_34}.tgz" - MONGODB_32="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian81-${VERSION_32}.tgz" - ;; - linux-debian-9*) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian92-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian92-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian92-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian92-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian92-${VERSION_36}.tgz" - ;; - linux-debian-10*) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian10-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-debian10-${VERSION_44}.tgz" - ;; - linux-ubuntu-18.04-s390x) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-ubuntu1804-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-ubuntu1804-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-ubuntu1804-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-ubuntu1804-${VERSION_40}.tgz" - ;; - linux-ubuntu-18.04-aarch64) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-aarch64-enterprise-ubuntu1804-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-aarch64-enterprise-ubuntu1804-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-aarch64-enterprise-ubuntu1804-${VERSION_42}.tgz" - ;; - linux-ubuntu-18.04-ppc64le) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-ubuntu1804-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-ubuntu1804-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-ubuntu1804-${VERSION_42}.tgz" - ;; - linux-ubuntu-18.04*) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1804-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1804-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1804-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1804-${VERSION_40}.tgz" - ;; - linux-ubuntu-16.04-s390x) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-ubuntu1604-latest.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-ubuntu1604-v4.0-latest.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-ubuntu1604-3.6.4.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-s390x-enterprise-ubuntu1604-3.4.14.tgz" - ;; - linux-ubuntu-16.04-ppc64le) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-ubuntu1604-latest.tgz" - # SERVER-37774 Removed support for Ubuntu 16.04 PPCLE - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-ubuntu1604-4.0.9.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-ubuntu1604-3.6.12.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-ppc64le-enterprise-ubuntu1604-3.4.20.tgz" - ;; - linux-ubuntu-16.04-aarch64) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-aarch64-enterprise-ubuntu1604-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-aarch64-enterprise-ubuntu1604-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-aarch64-enterprise-ubuntu1604-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-arm64-enterprise-ubuntu1604-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-arm64-enterprise-ubuntu1604-${VERSION_36}.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-arm64-enterprise-ubuntu1604-${VERSION_34}.tgz" - ;; - linux-ubuntu-16.04*) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1604-latest.tgz" - MONGODB_44="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1604-${VERSION_44}.tgz" - MONGODB_42="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1604-${VERSION_42}.tgz" - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1604-${VERSION_40}.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1604-${VERSION_36}.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1604-${VERSION_34}.tgz" - MONGODB_32="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1604-${VERSION_32}.tgz" - ;; - linux-ubuntu-14.04*) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1404-latest.tgz" - # SERVER-37765 Removed support for Ubuntu 14.04 - MONGODB_40="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1404-4.0.9.tgz" - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1404-3.6.12.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1404-3.4.20.tgz" - MONGODB_32="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1404-${VERSION_32}.tgz" - MONGODB_30="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1404-${VERSION_30}.tgz" - MONGODB_26="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1404-${VERSION_26}.tgz" - ;; - linux-ubuntu-12.04*) - MONGODB_LATEST="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1204-latest.tgz" - # SERVER-31535 removed support for Ubuntu 12. - MONGODB_36="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1204-3.6.3.tgz" - MONGODB_34="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1204-3.4.14.tgz" - MONGODB_32="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1204-3.2.19.tgz" - MONGODB_30="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1204-${VERSION_30}.tgz" - MONGODB_26="http://downloads.10gen.com/linux/mongodb-linux-x86_64-enterprise-ubuntu1204-${VERSION_26}.tgz" - MONGODB_24="http://downloads.10gen.com/linux/mongodb-linux-x86_64-subscription-ubuntu1204-${VERSION_24}.tgz" - ;; - windows32*) - EXTRACT="/cygdrive/c/Progra~2/7-Zip/7z.exe x" - MONGODB_32="https://fastdl.mongodb.org/win32/mongodb-win32-i386-${VERSION_32}.zip" - MONGODB_30="https://fastdl.mongodb.org/win32/mongodb-win32-i386-${VERSION_30}.zip" - MONGODB_26="https://fastdl.mongodb.org/win32/mongodb-win32-i386-${VERSION_26}.zip" - MONGODB_24="https://fastdl.mongodb.org/win32/mongodb-win32-i386-${VERSION_24}.zip" - ;; - windows64*) - # same as cygwin*-86-64 - EXTRACT="/cygdrive/c/Progra~2/7-Zip/7z.exe x" - MONGODB_LATEST="http://downloads.10gen.com/windows/mongodb-windows-x86_64-enterprise-latest.zip" - MONGODB_44="http://downloads.10gen.com/windows/mongodb-windows-x86_64-enterprise-${VERSION_44}.zip" - MONGODB_42="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_42}.zip" - MONGODB_40="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_40}.zip" - MONGODB_36="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_36}.zip" - MONGODB_34="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_34}.zip" - MONGODB_32="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_32}.zip" - MONGODB_30="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_30}.zip" - MONGODB_26="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_26}.zip" - MONGODB_24="https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-${VERSION_24}.zip" - ;; - cygwin*-x86_64) - EXTRACT="/cygdrive/c/Progra~2/7-Zip/7z.exe x" - MONGODB_LATEST="http://downloads.10gen.com/windows/mongodb-windows-x86_64-enterprise-latest.zip" - MONGODB_44="http://downloads.10gen.com/windows/mongodb-windows-x86_64-enterprise-${VERSION_44}.zip" - MONGODB_42="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_42}.zip" - MONGODB_40="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_40}.zip" - MONGODB_36="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_36}.zip" - MONGODB_34="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_34}.zip" - MONGODB_32="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_32}.zip" - MONGODB_30="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_30}.zip" - MONGODB_26="http://downloads.10gen.com/win32/mongodb-win32-x86_64-enterprise-windows-64-${VERSION_26}.zip" - MONGODB_24="https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-${VERSION_24}.zip" - ;; - cygwin*-i686) - EXTRACT="/cygdrive/c/Progra~1/7-Zip/7z.exe x" - MONGODB_32="https://fastdl.mongodb.org/win32/mongodb-win32-i386-${VERSION_32}.zip" - MONGODB_30="https://fastdl.mongodb.org/win32/mongodb-win32-i386-${VERSION_30}.zip" - MONGODB_26="https://fastdl.mongodb.org/win32/mongodb-win32-i386-${VERSION_26}.zip" - MONGODB_24="https://fastdl.mongodb.org/win32/mongodb-win32-i386-${VERSION_24}.zip" - ;; - esac - - # Fallback to generic Linux x86_64 builds (without SSL) when no platform specific link is available. - case "$_DISTRO" in - *linux*x86_64) - MONGODB_LATEST=${MONGODB_LATEST:-"http://downloads.mongodb.org/linux/mongodb-linux-x86_64-latest.tgz"} - # SERVER-37316 Removed support for generic linux builds. - MONGODB_42=${MONGODB_42:-""} - MONGODB_40=${MONGODB_40:-"http://downloads.mongodb.org/linux/mongodb-linux-x86_64-${VERSION_40}.tgz"} - MONGODB_36=${MONGODB_36:-"http://downloads.mongodb.org/linux/mongodb-linux-x86_64-${VERSION_36}.tgz"} - MONGODB_34=${MONGODB_34:-"http://downloads.mongodb.org/linux/mongodb-linux-x86_64-${VERSION_34}.tgz"} - MONGODB_32=${MONGODB_32:-"http://downloads.mongodb.org/linux/mongodb-linux-x86_64-${VERSION_32}.tgz"} - MONGODB_30=${MONGODB_30:-"http://downloads.mongodb.org/linux/mongodb-linux-x86_64-${VERSION_30}.tgz"} - MONGODB_26=${MONGODB_26:-"http://downloads.mongodb.org/linux/mongodb-linux-x86_64-${VERSION_26}.tgz"} - MONGODB_24=${MONGODB_24:-"http://downloads.mongodb.org/linux/mongodb-linux-x86_64-${VERSION_24}.tgz"} - ;; - esac - - # PYTHON-2238 On Archlinux MongoDB <= 3.2 requires LC_ALL=C. - case "$_DISTRO" in - linux-arch-*) - case "$_VERSION" in - 3.2) export LC_ALL=C ;; - 3.0) export LC_ALL=C ;; - 2.6) export LC_ALL=C ;; - 2.4) export LC_ALL=C ;; - esac - ;; - esac - - case "$_VERSION" in - latest) MONGODB_DOWNLOAD_URL=$MONGODB_LATEST ;; - 4.4) MONGODB_DOWNLOAD_URL=$MONGODB_44 ;; - 4.2) MONGODB_DOWNLOAD_URL=$MONGODB_42 ;; - 4.0) MONGODB_DOWNLOAD_URL=$MONGODB_40 ;; - 3.6) MONGODB_DOWNLOAD_URL=$MONGODB_36 ;; - 3.4) MONGODB_DOWNLOAD_URL=$MONGODB_34 ;; - 3.2) MONGODB_DOWNLOAD_URL=$MONGODB_32 ;; - 3.0) MONGODB_DOWNLOAD_URL=$MONGODB_30 ;; - 2.6) MONGODB_DOWNLOAD_URL=$MONGODB_26 ;; - 2.4) MONGODB_DOWNLOAD_URL=$MONGODB_24 ;; - esac - - [ -z "$MONGODB_DOWNLOAD_URL" ] && MONGODB_DOWNLOAD_URL="Unknown version: $_VERSION for $_DISTRO" - - echo $MONGODB_DOWNLOAD_URL -} - -download_and_extract () -{ - MONGODB_DOWNLOAD_URL=$1 - EXTRACT=$2 - - cd $DRIVERS_TOOLS - curl --retry 8 -sS $MONGODB_DOWNLOAD_URL --max-time 300 --output mongodb-binaries.tgz - $EXTRACT mongodb-binaries.tgz - - rm mongodb-binaries.tgz - mv mongodb* mongodb - chmod -R +x mongodb - find . -name vcredist_x64.exe -exec {} /install /quiet \; - ./mongodb/bin/mongod --version - cd - -} diff --git a/.evergreen/generate_task_config.py b/.evergreen/generate_task_config.py deleted file mode 100644 index 3820cc400..000000000 --- a/.evergreen/generate_task_config.py +++ /dev/null @@ -1,31 +0,0 @@ -import itertools - - -TASK_TEMPLATE = ''' - - name: "test-{version}-{topology}" - tags: ["{version}", "{topology}"] - commands: - - func: "bootstrap mongo-orchestration" - vars: - VERSION: "{version}" - TOPOLOGY: "{mo_topology}" - - func: "run tests"''' - -MONGODB_VERSIONS = ['2.4', '2.6', '3.0', '3.2', '3.4', 'latest'] -TOPOLOGY_OPTIONS = ['standalone', 'replica_set', 'sharded_cluster'] - - -def create_task(version, topology): - mo_topology= topology - # mongo-orchestration uses 'server' as the name for 'standalone' - if mo_topology == 'standalone': - mo_topology = 'server' - return TASK_TEMPLATE.format(**locals()) - - -tasks = [] -for version, topology in itertools.product(MONGODB_VERSIONS, - TOPOLOGY_OPTIONS): - tasks.append(create_task(version, topology)) - -print('\n'.join(tasks)) diff --git a/.evergreen/install-dependencies.sh b/.evergreen/install-dependencies.sh index 9ccd2dc10..587558c7c 100644 --- a/.evergreen/install-dependencies.sh +++ b/.evergreen/install-dependencies.sh @@ -1,18 +1,39 @@ #!/bin/sh -set -o xtrace # Write all commands first to stderr set -o errexit # Exit the script with error if any of the commands fail -install_extension () +set_php_version () { - # Workaround to get PECL running on PHP 7.0 - export PHP_PEAR_PHP_BIN=${PHP_PATH}/bin/php - export PHP_PEAR_INSTALL_DIR=${PHP_PATH}/lib/php + PHP_VERSION=$1 + + if [ ! -d "/opt/php" ]; then + echo "PHP is not available" + exit 1 + fi + + if [ -d "/opt/php/${PHP_VERSION}-64bit/bin" ]; then + export PHP_PATH="/opt/php/${PHP_VERSION}-64bit" + else + # Try to find the newest version matching our constant + export PHP_PATH=`find /opt/php/ -maxdepth 1 -type d -name "${PHP_VERSION}.*-64bit" -print | sort -V -r | head -1` + fi + + if [ ! -d "$PHP_PATH" ]; then + echo "Could not find PHP binaries for version ${PHP_VERSION}. Listing available versions..." + ls -1 /opt/php + exit 1 + fi + + echo 'PHP_PATH: "'$PHP_PATH'"' > php-expansion.yml + export PATH=$PHP_PATH/bin:$PATH +} +install_extension () +{ rm -f ${PHP_PATH}/lib/php.ini - if [ "x${DRIVER_BRANCH}" != "x" ] || [ "x${DRIVER_REPO}" != "x" ]; then - CLONE_REPO=${DRIVER_REPO:-https://github.com/mongodb/mongo-php-driver} - CHECKOUT_BRANCH=${DRIVER_BRANCH:-master} + if [ "x${EXTENSION_BRANCH}" != "x" ] || [ "x${EXTENSION_REPO}" != "x" ]; then + CLONE_REPO=${EXTENSION_REPO:-https://github.com/mongodb/mongo-php-driver} + CHECKOUT_BRANCH=${EXTENSION_BRANCH:-master} echo "Compiling driver branch ${CHECKOUT_BRANCH} from repository ${CLONE_REPO}" @@ -29,25 +50,41 @@ install_extension () make install cd ${PROJECT_DIRECTORY} - elif [ "x${DRIVER_VERSION}" != "x" ]; then - echo "Installing driver version ${DRIVER_VERSION} from PECL" - pecl install -f mongodb-${DRIVER_VERSION} + elif [ "x${EXTENSION_VERSION}" != "x" ]; then + echo "Installing driver version ${EXTENSION_VERSION} from PECL" + pecl install -f mongodb-${EXTENSION_VERSION} else echo "Installing latest driver version from PECL" pecl install -f mongodb fi sudo cp ${PROJECT_DIRECTORY}/.evergreen/config/php.ini ${PHP_PATH}/lib/php.ini + + php --ri mongodb +} + +install_composer () +{ + EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')" + php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" + ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" + + if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then + >&2 echo 'ERROR: Invalid installer checksum' + rm composer-setup.php + exit 1 + fi + + php composer-setup.php --quiet + rm composer-setup.php } -DIR=$(dirname $0) # Functions to fetch MongoDB binaries -. $DIR/download-mongodb.sh +. ${DRIVERS_TOOLS}/.evergreen/download-mongodb.sh OS=$(uname -s | tr '[:upper:]' '[:lower:]') get_distro -# See .evergreen/download-mongodb.sh for most possible values case "$DISTRO" in cygwin*) echo "Install Windows dependencies" @@ -85,17 +122,8 @@ case "$DEPENDENCIES" in ;; esac -PHP_PATH=/opt/php/${PHP_VERSION}-64bit -OLD_PATH=$PATH -PATH=$PHP_PATH/bin:$OLD_PATH - +set_php_version $PHP_VERSION install_extension - -php --ri mongodb - -# Install composer -php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" -php composer-setup.php -php -r "unlink('composer-setup.php');" +install_composer php composer.phar update $COMPOSER_FLAGS diff --git a/.evergreen/make-docs.sh b/.evergreen/make-docs.sh deleted file mode 100644 index f61da5a4b..000000000 --- a/.evergreen/make-docs.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with error if any of the commands fail - - -mkdir -p doc/html || true - - -cat < doc/html/index.html > doc/html/intro.html - - - -EOT - -cat <> doc/html/index.html -This is an example of a doc page automatically uploaded to evergreen so you can see your docs rendered. -The evergreen URL differs for patch builds and normal builds -EOT -cat <> doc/html/intro.html -This page is never actually uploaded by evergreen, only the index page was uploaded. -Thats why there is a link to the index page ("Rendered docs") while this is not an actual artifact. -This page was uploaded seperately with the s3cmd tools -EOT diff --git a/.evergreen/make-release.sh b/.evergreen/make-release.sh deleted file mode 100644 index ef6e2af7e..000000000 --- a/.evergreen/make-release.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with error if any of the commands fail - - -echo "Creating Release Archive Bundle RPM" - -echo "The release archive will have the version automatically derived from the nearest git tag for patchbuilds, otherwise 'latest' is used" - -echo "The release file should be called $PROJECT.tar.gz" - - -cd .. && tar czf $PROJECT.tar.gz src diff --git a/.evergreen/orchestration/db/.empty b/.evergreen/orchestration/db/.empty deleted file mode 100644 index e69de29bb..000000000 diff --git a/.evergreen/orchestration/lib/client.pem b/.evergreen/orchestration/lib/client.pem deleted file mode 100644 index 5b0700109..000000000 --- a/.evergreen/orchestration/lib/client.pem +++ /dev/null @@ -1,48 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAsNS8UEuin7/K29jXfIOLpIoh1jEyWVqxiie2Onx7uJJKcoKo -khA3XeUnVN0k6X5MwYWcN52xcns7LYtyt06nRpTG2/emoV44w9uKTuHsvUbiOwSV -m/ToKQQ4FUFZoqorXH+ZmJuIpJNfoW+3CkE1vEDCIecIq6BNg5ySsPtvSuSJHGjp -mc7/5ZUDvFE2aJ8QbJU3Ws0HXiEb6ymi048LlzEL2VKX3w6mqqh+7dcZGAy7qYk2 -5FZ9ktKvCeQau7mTyU1hsPrKFiKtMN8Q2ZAItX13asw5/IeSTq2LgLFHlbj5Kpq4 -GmLdNCshzH5X7Ew3IYM8EHmsX8dmD6mhv7vpVwIDAQABAoIBABOdpb4qhcG+3twA -c/cGCKmaASLnljQ/UU6IFTjrsjXJVKTbRaPeVKX/05sgZQXZ0t3s2mV5AsQ2U1w8 -Cd+3w+qaemzQThW8hAOGCROzEDX29QWi/o2sX0ydgTMqaq0Wv3SlWv6I0mGfT45y -/BURIsrdTCvCmz2erLqa1dL4MWJXRFjT9UTs5twlecIOM2IHKoGGagFhymRK4kDe -wTRC9fpfoAgyfus3pCO/wi/F8yKGPDEwY+zgkhrJQ+kSeki7oKdGD1H540vB8gRt -EIqssE0Y6rEYf97WssQlxJgvoJBDSftOijS6mwvoasDUwfFqyyPiirawXWWhHXkc -DjIi/XECgYEA5xfjilw9YyM2UGQNESbNNunPcj7gDZbN347xJwmYmi9AUdPLt9xN -3XaMqqR22k1DUOxC/5hH0uiXir7mDfqmC+XS/ic/VOsa3CDWejkEnyGLiwSHY502 -wD/xWgHwUiGVAG9HY64vnDGm6L3KGXA2oqxanL4V0+0+Ht49pZ16i8sCgYEAw+Ox -CHGtpkzjCP/z8xr+1VTSdpc/4CP2HONnYopcn48KfQnf7Nale69/1kZpypJlvQSG -eeA3jMGigNJEkb8/kaVoRLCisXcwLc0XIfCTeiK6FS0Ka30D/84Qm8UsHxRdpGkM -kYITAa2r64tgRL8as4/ukeXBKE+oOhX43LeEfyUCgYBkf7IX2Ndlhsm3GlvIarxy -NipeP9PGdR/hKlPbq0OvQf9R1q7QrcE7H7Q6/b0mYNV2mtjkOQB7S2WkFDMOP0P5 -BqDEoKLdNkV/F9TOYH+PCNKbyYNrodJOt0Ap6Y/u1+Xpw3sjcXwJDFrO+sKqX2+T -PStG4S+y84jBedsLbDoAEwKBgQCTz7/KC11o2yOFqv09N+WKvBKDgeWlD/2qFr3w -UU9K5viXGVhqshz0k5z25vL09Drowf1nAZVpFMO2SPOMtq8VC6b+Dfr1xmYIaXVH -Gu1tf77CM9Zk/VSDNc66e7GrUgbHBK2DLo+A+Ld9aRIfTcSsMbNnS+LQtCrQibvb -cG7+MQKBgQCY11oMT2dUekoZEyW4no7W5D74lR8ztMjp/fWWTDo/AZGPBY6cZoZF -IICrzYtDT/5BzB0Jh1f4O9ZQkm5+OvlFbmoZoSbMzHL3oJCBOY5K0/kdGXL46WWh -IRJSYakNU6VIS7SjDpKgm9D8befQqZeoSggSjIIULIiAtYgS80vmGA== ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDgzCCAmugAwIBAgIDAxOUMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIzNTU1NFoXDTM5MDUyMjIzNTU1NFowaTEP -MA0GA1UEAxMGY2xpZW50MRAwDgYDVQQLEwdEcml2ZXJzMQwwCgYDVQQKEwNNREIx -FjAUBgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3JrMQswCQYD -VQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALDUvFBLop+/ -ytvY13yDi6SKIdYxMllasYontjp8e7iSSnKCqJIQN13lJ1TdJOl+TMGFnDedsXJ7 -Oy2LcrdOp0aUxtv3pqFeOMPbik7h7L1G4jsElZv06CkEOBVBWaKqK1x/mZibiKST -X6FvtwpBNbxAwiHnCKugTYOckrD7b0rkiRxo6ZnO/+WVA7xRNmifEGyVN1rNB14h -G+spotOPC5cxC9lSl98Opqqofu3XGRgMu6mJNuRWfZLSrwnkGru5k8lNYbD6yhYi -rTDfENmQCLV9d2rMOfyHkk6ti4CxR5W4+SqauBpi3TQrIcx+V+xMNyGDPBB5rF/H -Zg+pob+76VcCAwEAAaMkMCIwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUF -BwMCMA0GCSqGSIb3DQEBCwUAA4IBAQAqRcLAGvYMaGYOV4HJTzNotT2qE0I9THNQ -wOV1fBg69x6SrUQTQLjJEptpOA288Wue6Jt3H+p5qAGV5GbXjzN/yjCoItggSKxG -Xg7279nz6/C5faoIKRjpS9R+MsJGlttP9nUzdSxrHvvqm62OuSVFjjETxD39DupE -YPFQoHOxdFTtBQlc/zIKxVdd20rs1xJeeU2/L7jtRBSPuR/Sk8zot7G2/dQHX49y -kHrq8qz12kj1T6XDXf8KZawFywXaz0/Ur+fUYKmkVk1T0JZaNtF4sKqDeNE4zcns -p3xLVDSl1Q5Gwj7bgph9o4Hxs9izPwiqjmNaSjPimGYZ399zcurY ------END CERTIFICATE----- diff --git a/.evergreen/run-atlas-proxy.sh b/.evergreen/run-atlas-proxy.sh deleted file mode 100755 index edfdb7bc9..000000000 --- a/.evergreen/run-atlas-proxy.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/sh -# -# This script uses the Atlas proxy project's own evergreen launch script -# to build and launch an Atlas proxy for testing. It works directly from -# their master branch, so may fail if they break something. -# -# There is no corresponding 'shutdown' script; the Atlas proxy project -# relies on Evergreen to terminate processes and clean up when tasks end, -# so we do the same. -# -# The URI is harded coded as: -# mongodb://user:pencil@host5.local.10gen.cc:9900/admin?replicaSet=benchmark -# -# Connections requires SSL and the CA file is 'main/ca.pem' in the atlasproxy repo. -# -# If this fails, check the 'main/test.sh' file in the atlasproxy repo for -# possible changes. -# -# Installation of Go dependencies appears to require a newer git. 1.7 on -# rhel62 failed, but 2.0 on ubuntu1604 worked. The atlasproxy project -# itself tests on rhel70. -# -# This script expects the following environment variables: -# -# DRIVERS_TOOLS (required) - absolute path to the checked out -# driver-evergreen-tools repository -# -# MONGODB_VERSION - version of MongoDB to download and use. For Atlas -# Proxy, must be "3.4" or "latest". Defaults to "3.4". - -set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with error if any of the commands fail - -MONGODB_VERSION=${MONGODB_VERSION:-"3.4"} - -ORIG_DIR="$(pwd)" - -#--------------------------------------------------------------------------# -# Downlaod MongoDB Binary -#--------------------------------------------------------------------------# - -DL_START=$(date +%s) -DIR=$(dirname $0) - -# Load download helper functions -. $DIR/download-mongodb.sh - -# set $DISTRO -get_distro - -# set $MONGODB_DOWNLOAD_URL and $EXTRACT -get_mongodb_download_url_for "$DISTRO" "$MONGODB_VERSION" - -# extracts to $DRIVERS_TOOLS/mongodb -rm -rf "$DRIVERS_TOOLS/mongodb" -download_and_extract "$MONGODB_DOWNLOAD_URL" "$EXTRACT" -DL_END=$(date +%s) - -#--------------------------------------------------------------------------# -# Clone Atlas Proxy repo and launch it -#--------------------------------------------------------------------------# - -AP_START=$(date +%s) - -cd "$ORIG_DIR" -rm -rf atlasproxy -git clone git@github.com:10gen/atlasproxy.git -cd atlasproxy - -# This section copied from atlasproxy's .evergreen.yml: <<< -export PATH="/opt/golang/go1.11/bin:$PATH" -export GOROOT="/opt/golang/go1.11" -export GOPATH=`pwd`/.gopath -go version -./gpm -export MONGO_DIR="$DRIVERS_TOOLS/mongodb/bin" -cd main -./start_test_proxies_and_mtms.sh -# >>> end of copy - -AP_END=$(date +%s) - -# Write results file -DL_ELAPSED=$(expr $DL_END - $DL_START) -AP_ELAPSED=$(expr $AP_END - $AP_START) -cat <> $DRIVERS_TOOLS/results.json -{"results": [ - { - "status": "PASS", - "test_file": "AtlasProxy Start", - "start": $AP_START, - "end": $AP_END, - "elapsed": $AP_ELAPSED - }, - { - "status": "PASS", - "test_file": "Download MongoDB", - "start": $DL_START, - "end": $DL_END, - "elapsed": $DL_ELAPSED - } -]} - -EOT diff --git a/.evergreen/run-orchestration.sh b/.evergreen/run-orchestration.sh deleted file mode 100755 index d969aad16..000000000 --- a/.evergreen/run-orchestration.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/bin/sh -set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with error if any of the commands fail - - -AUTH=${AUTH:-noauth} -SSL=${SSL:-nossl} -TOPOLOGY=${TOPOLOGY:-server} -STORAGE_ENGINE=${STORAGE_ENGINE} -# Set to a non-empty string to use the /disableTestCommands.json -# cluster config, eg DISABLE_TEST_COMMANDS=1 -DISABLE_TEST_COMMANDS=${DISABLE_TEST_COMMANDS} -MONGODB_VERSION=${MONGODB_VERSION:-latest} - -DL_START=$(date +%s) -DIR=$(dirname $0) -# Functions to fetch MongoDB binaries -. $DIR/download-mongodb.sh - -get_distro -if [ -z "$MONGODB_DOWNLOAD_URL" ]; then - get_mongodb_download_url_for "$DISTRO" "$MONGODB_VERSION" -else - # Even though we have the MONGODB_DOWNLOAD_URL, we still call this to get the proper EXTRACT variable - get_mongodb_download_url_for "$DISTRO" -fi -download_and_extract "$MONGODB_DOWNLOAD_URL" "$EXTRACT" - -DL_END=$(date +%s) -MO_START=$(date +%s) - -ORCHESTRATION_FILE=${ORCHESTRATION_FILE} -# If no orchestration file was specified, build up the name based on configuration parameters. -if [ -z "$ORCHESTRATION_FILE" ]; then - ORCHESTRATION_FILE="basic" - if [ "$AUTH" = "auth" ]; then - ORCHESTRATION_FILE="auth" - fi - - if [ "$SSL" != "nossl" ]; then - ORCHESTRATION_FILE="${ORCHESTRATION_FILE}-ssl" - fi - - # disableTestCommands files do not exist for different auth or ssl modes. - if [ ! -z "$DISABLE_TEST_COMMANDS" ]; then - ORCHESTRATION_FILE="disableTestCommands" - fi - - # Storage engine config files do not exist for different auth or ssl modes. - if [ ! -z "$STORAGE_ENGINE" ]; then - ORCHESTRATION_FILE="$STORAGE_ENGINE" - fi - - ORCHESTRATION_FILE="${ORCHESTRATION_FILE}.json" -fi - -TOOLS_ORCHESTRATION_FILE="$MONGO_ORCHESTRATION_HOME/configs/${TOPOLOGY}s/${ORCHESTRATION_FILE}" -CUSTOM_ORCHESTRATION_FILE="$DIR/orchestration/configs/${TOPOLOGY}s/${ORCHESTRATION_FILE}" - -if [ -f "$CUSTOM_ORCHESTRATION_FILE" ]; then - export ORCHESTRATION_FILE="$CUSTOM_ORCHESTRATION_FILE" -elif [ -f "$TOOLS_ORCHESTRATION_FILE" ]; then - export ORCHESTRATION_FILE="$TOOLS_ORCHESTRATION_FILE" -else - echo "Could not find orchestration file $ORCHESTRATION_FILE" - exit 1 -fi - -export ORCHESTRATION_URL="http://localhost:8889/v1/${TOPOLOGY}s" - -# Start mongo-orchestration -sh $DIR/start-orchestration.sh "$MONGO_ORCHESTRATION_HOME" - -pwd -if ! curl --silent --show-error --data @"$ORCHESTRATION_FILE" "$ORCHESTRATION_URL" --max-time 600 --fail -o tmp.json; then - echo Failed to start cluster, see $MONGO_ORCHESTRATION_HOME/out.log: - cat $MONGO_ORCHESTRATION_HOME/out.log - echo Failed to start cluster, see $MONGO_ORCHESTRATION_HOME/server.log: - cat $MONGO_ORCHESTRATION_HOME/server.log - exit 1 -fi -cat tmp.json -URI=$(python -c 'import sys, json; j=json.load(open("tmp.json")); print(j["mongodb_auth_uri" if "mongodb_auth_uri" in j else "mongodb_uri"])' | tr -d '\r') -echo 'MONGODB_URI: "'$URI'"' > mo-expansion.yml -echo "Cluster URI: $URI" - -MO_END=$(date +%s) -MO_ELAPSED=$(expr $MO_END - $MO_START) -DL_ELAPSED=$(expr $DL_END - $DL_START) -cat <> $DRIVERS_TOOLS/results.json -{"results": [ - { - "status": "PASS", - "test_file": "Orchestration", - "start": $MO_START, - "end": $MO_END, - "elapsed": $MO_ELAPSED - }, - { - "status": "PASS", - "test_file": "Download MongoDB", - "start": $DL_START, - "end": $DL_END, - "elapsed": $DL_ELAPSED - } -]} - -EOT diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index 423466503..df32fb941 100755 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -1,35 +1,109 @@ #!/bin/sh -set -o xtrace # Write all commands first to stderr set -o errexit # Exit the script with error if any of the commands fail -# Supported/used environment variables: -# AUTH Set to enable authentication. Defaults to "noauth" -# SSL Set to enable SSL. Defaults to "nossl" -# MONGODB_URI Set the suggested connection MONGODB_URI (including credentials and topology info) -# MARCH Machine Architecture. Defaults to lowercase uname -m +# Supported environment variables +API_VERSION=${API_VERSION:-} # Optional API_VERSION environment variable +CRYPT_SHARED_LIB_PATH="${CRYPT_SHARED_LIB_PATH:-}" # Optional path to crypt_shared library +DRIVER_MONGODB_VERSION=${DRIVER_MONGODB_VERSION:-} # Required if IS_MATRIX_TESTING is "true" +IS_MATRIX_TESTING=${IS_MATRIX_TESTING:-} # Specify "true" to enable matrix testing. Defaults to empty string. If "true", DRIVER_MONGODB_VERSION and MONGODB_VERSION will also be checked. +MONGODB_URI=${MONGODB_URI:-} # Connection string (including credentials and topology info) +MONGODB_SINGLE_MONGOS_LB_URI=${MONGODB_SINGLE_MONGOS_LB_URI:-} # Single-mongos LB connection string +MONGODB_MULTI_MONGOS_LB_URI=${MONGODB_MULTI_MONGOS_LB_URI:-} # Multi-mongos LB connection string +MONGODB_VERSION=${MONGODB_VERSION:-} # Required if IS_MATRIX_TESTING is "true" +SKIP_CRYPT_SHARED="${SKIP_CRYPT_SHARED:-no}" # Specify "yes" to ignore CRYPT_SHARED_LIB_PATH. Defaults to "no" +SSL=${SSL:-no} # Specify "yes" to enable SSL. Defaults to "no" +TESTS=${TESTS:-} # Optional test group. Defaults to all tests +# For matrix testing, we have to determine the correct driver version +if [ "${IS_MATRIX_TESTING}" = "true" ]; then + case "${DRIVER_MONGODB_VERSION}" in + '4.4') + export EXTENSION_VERSION='1.8.2' + ;; + '4.2') + export EXTENSION_VERSION='1.6.1' + ;; + '4.0') + export EXTENSION_VERSION='1.5.5' + ;; + esac -AUTH=${AUTH:-noauth} -SSL=${SSL:-nossl} -MONGODB_URI=${MONGODB_URI:-} -TESTS=${TESTS:-} + case "${MONGODB_VERSION}" in + latest) + MONGODB_VERSION_NUMBER='5.0' + ;; + *) + MONGODB_VERSION_NUMBER=$MONGODB_VERSION + ;; + esac -OS=$(uname -s | tr '[:upper:]' '[:lower:]') -[ -z "$MARCH" ] && MARCH=$(uname -m | tr '[:upper:]' '[:lower:]') + PHPUNIT_OPTS="--dont-report-useless-tests --exclude-group matrix-testing-exclude-server-${MONGODB_VERSION_NUMBER}-driver-${DRIVER_MONGODB_VERSION},matrix-testing-exclude-server-${MONGODB_VERSION_NUMBER}-driver-${DRIVER_MONGODB_VERSION}-topology-${TOPOLOGY}" -echo "Running $AUTH tests over $SSL, connecting to $MONGODB_URI" + DIR=$(dirname $0) + . $DIR/install-dependencies.sh +fi -OLD_PATH=$PATH -PATH=/opt/php/${PHP_VERSION}-64bit/bin:$OLD_PATH +# Enable verbose output to see skipped and incomplete tests +PHPUNIT_OPTS="${PHPUNIT_OPTS} -v --configuration phpunit.evergreen.xml" -# Run the tests, and store the results in a Evergreen compatible JSON results file +if [ "$SSL" = "yes" ]; then + SSL_OPTS="ssl=true&sslallowinvalidcertificates=true" + + # Determine if MONGODB_URI already has a query string + SUFFIX=$(echo "$MONGODB_URI" | grep -Eo "\?(.*)" | cat) + + if [ -z "$SUFFIX" ]; then + MONGODB_URI="${MONGODB_URI}/?${SSL_OPTS}" + else + MONGODB_URI="${MONGODB_URI}&${SSL_OPTS}" + fi + + # Assume LB URIs already have a query string (e.g. "?loadBalanced=true") + if [ -n "${MONGODB_SINGLE_MONGOS_LB_URI}" ]; then + MONGODB_SINGLE_MONGOS_LB_URI="${MONGODB_SINGLE_MONGOS_LB_URI}&${SSL_OPTS}" + fi + + if [ -n "${MONGODB_MULTI_MONGOS_LB_URI}" ]; then + MONGODB_MULTI_MONGOS_LB_URI="${MONGODB_MULTI_MONGOS_LB_URI}&${SSL_OPTS}" + fi +fi + +echo "Running tests with URI: $MONGODB_URI" + +# Disable PHPUnit test failures due to deprecations +# See: https://symfony.com/doc/current/components/phpunit_bridge.html#internal-deprecations +export SYMFONY_DEPRECATIONS_HELPER=999999 + +# Export environment vars that may be referenced by the test suite +export API_VERSION="${API_VERSION}" +export CRYPT_SHARED_LIB_PATH="${CRYPT_SHARED_LIB_PATH}" +export MONGODB_URI="${MONGODB_URI}" +export MONGODB_SINGLE_MONGOS_LB_URI="${MONGODB_SINGLE_MONGOS_LB_URI}" +export MONGODB_MULTI_MONGOS_LB_URI="${MONGODB_MULTI_MONGOS_LB_URI}" + +# Run the tests, and store the results in a junit result file case "$TESTS" in atlas-data-lake*) - MONGODB_URI="mongodb://mhuser:pencil@127.0.0.1:27017" - php vendor/bin/simple-phpunit --configuration phpunit.evergreen.xml --testsuite "Atlas Data Lake Test Suite" + php vendor/bin/simple-phpunit $PHPUNIT_OPTS --testsuite "Atlas Data Lake Test Suite" + ;; + + csfle) + php vendor/bin/simple-phpunit $PHPUNIT_OPTS --group csfle + ;; + + csfle-without-aws-creds) + php vendor/bin/simple-phpunit $PHPUNIT_OPTS --group csfle-without-aws-creds + ;; + + versioned-api) + php vendor/bin/simple-phpunit $PHPUNIT_OPTS --group versioned-api + ;; + + serverless) + php vendor/bin/simple-phpunit $PHPUNIT_OPTS --group serverless ;; *) - php vendor/bin/simple-phpunit --configuration phpunit.evergreen.xml + php vendor/bin/simple-phpunit $PHPUNIT_OPTS ;; esac diff --git a/.evergreen/start-orchestration.sh b/.evergreen/start-orchestration.sh deleted file mode 100644 index 2197f2d40..000000000 --- a/.evergreen/start-orchestration.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/bin/sh - -if [ "$#" -ne 1 ]; then - echo "$0 requires one argument: " - echo "For example: $0 /tmp/mongo-orchestration-home" - exit 1 -fi - -set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with error if any of the commands fail - - -MONGO_ORCHESTRATION_HOME="$1" - -echo From shell `date` > $MONGO_ORCHESTRATION_HOME/server.log - -cd "$MONGO_ORCHESTRATION_HOME" -# Setup or use the existing virtualenv for mongo-orchestration. -# Prefer using Python 3 from the toolchain over the default system python. -# We may still fallback to using an old version of Python here. -# Many of the Linux distros in Evergreen ship Python 2.6 as the -# system Python. Core libraries installed by virtualenv (setuptools, -# pip, wheel) have dropped, or soon will drop, support for Python -# 2.6. Starting with version 14, virtualenv upgrades these libraries -# to the latest available on pypi when creating the virtual environment -# unless you pass --never-download. -PYTHON=$(command -v /opt/mongodbtoolchain/v2/bin/python3 || command -v python3 || command -v python) -$PYTHON -c 'import sys; print(sys.version)' -$PYTHON -c 'import ssl; print(ssl.OPENSSL_VERSION)' - -if [ -f venv/bin/activate ]; then - . venv/bin/activate -elif [ -f venv/Scripts/activate ]; then - . venv/Scripts/activate -elif $PYTHON -m virtualenv --system-site-packages --never-download venv || virtualenv --system-site-packages --never-download venv; then - if [ -f venv/bin/activate ]; then - . venv/bin/activate - elif [ -f venv/Scripts/activate ]; then - . venv/Scripts/activate - fi - # Install from github to get the latest mongo-orchestration. - pip install --upgrade 'git+git://github.com/mongodb/mongo-orchestration@master' - pip list -fi -cd - - -ORCHESTRATION_ARGUMENTS="-e default -f $MONGO_ORCHESTRATION_HOME/orchestration.config --socket-timeout-ms=60000 --bind=127.0.0.1 --enable-majority-read-concern" -if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin - ORCHESTRATION_ARGUMENTS="$ORCHESTRATION_ARGUMENTS -s wsgiref" -fi - -# Forcibly kill the process listening on port 8889, most likey a wild -# mongo-orchestration left running from a previous task. -if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin - OLD_MO_PID=$(netstat -ano | grep ':8889 .* LISTENING' | awk '{print $5}' | tr -d '[:space:]') - if [ ! -z "$OLD_MO_PID" ]; then - taskkill /F /T /PID "$OLD_MO_PID" || true - fi -else - OLD_MO_PID=$(lsof -t -i:8889 || true) - if [ ! -z "$OLD_MO_PID" ]; then - kill -9 "$OLD_MO_PID" || true - fi -fi - -mongo-orchestration $ORCHESTRATION_ARGUMENTS start > $MONGO_ORCHESTRATION_HOME/out.log 2>&1 < /dev/null & - -ls -la $MONGO_ORCHESTRATION_HOME - -sleep 5 -if ! curl http://localhost:8889/ --silent --show-error --max-time 120 --fail; then - echo Failed to start mongo-orchestration, see $MONGO_ORCHESTRATION_HOME/out.log: - cat $MONGO_ORCHESTRATION_HOME/out.log - echo Failed to start mongo-orchestration, see $MONGO_ORCHESTRATION_HOME/server.log: - cat $MONGO_ORCHESTRATION_HOME/server.log - exit 1 -fi -sleep 5 diff --git a/.evergreen/stop-orchestration.sh b/.evergreen/stop-orchestration.sh deleted file mode 100755 index 5c01d1580..000000000 --- a/.evergreen/stop-orchestration.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with error if any of the commands fail - -cd "$MONGO_ORCHESTRATION_HOME" -# source the mongo-orchestration virtualenv if it exists -if [ -f venv/bin/activate ]; then - . venv/bin/activate -elif [ -f venv/Scripts/activate ]; then - . venv/Scripts/activate -fi -mongo-orchestration stop diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..eb9a2b8f3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,13 @@ +.* export-ignore +*.md export-ignore +tests export-ignore +docs export-ignore +examples export-ignore +mongo-orchestration export-ignore +tools export-ignore +Makefile export-ignore +phpcs.xml.dist export-ignore +phpunit.evergreen.xml export-ignore +phpunit.xml.dist export-ignore +psalm.xml.dist export-ignore +psalm-baseline.xml export-ignore diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index fa2163dbb..df9469e7a 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -1,6 +1,6 @@ --- name: Bug Report -about: Create a report to help us improve +about: Report a bug for the MongoDB PHP library. title: '' labels: '' assignees: '' @@ -16,7 +16,7 @@ If you've identified a security vulnerability in a driver or any other MongoDB project, please create a vulnerability report[^2]. [^1]: https://github.com/mongodb/mongo-php-driver -[^2]: https://docs.mongodb.org/manual/tutorial/create-a-vulnerability-report +[^2]: https://mongodb.com/docs/manual/tutorial/create-a-vulnerability-report --> ### Bug Report @@ -67,7 +67,7 @@ reproduced with PHP's built-in web server[^3]. If not, please share your web server version and any relevant configuration details in the Environment section above. -[^3]: http://php.net/manual/en/features.commandline.webserver.php +[^3]: https://php.net/manual/en/features.commandline.webserver.php --> ### Expected and Actual Behavior @@ -90,5 +90,5 @@ If the issue relates to internal driver behavior (e.g. connection issues), please include a debug log[^5]. This may be generated by setting the `mongodb.debug` INI option to "stderr" or a directory (useful for web SAPIs). -[^5]: https://www.php.net/manual/en/mongodb.configuration.php#ini.mongodb.debug +[^5]: https://php.net/manual/en/mongodb.configuration.php#ini.mongodb.debug --> diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..e3377e688 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,12 @@ +blank_issues_enabled: false + +contact_links: + - name: New issue on mongodb/mongo-php-driver + url: https://github.com/mongodb/mongo-php-driver/issues/new/choose + about: Experiencing a problem with the MongoDB PHP extension? Please report it through the mongo-php-driver repository. + - name: MongoDB Developer Community Forums + url: https://developer.mongodb.com/community/forums/ + about: For questions, discussions, or general technical support, visit the MongoDB Community Forums. The MongoDB Community Forums are a centralized place to connect with other MongoDB users, ask questions, and get answers. + - name: Report a Security Vulnerability + url: https://mongodb.com/docs/manual/tutorial/create-a-vulnerability-report + about: If you believe you have discovered a vulnerability in MongoDB products or have experienced a security incident related to MongoDB products, please report the issue to aid in its resolution. diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml new file mode 100644 index 000000000..c3e8d5d49 --- /dev/null +++ b/.github/workflows/coding-standards.yml @@ -0,0 +1,65 @@ +name: "Coding Standards" + +on: + pull_request: + branches: + - "v*.*" + - "master" + - "feature/*" + paths-ignore: + - "docs/**" + push: + branches: + - "v*.*" + - "master" + - "feature/*" + paths-ignore: + - "docs/**" + +env: + PHP_VERSION: "8.2" + DRIVER_VERSION: "stable" + +jobs: + phpcs: + name: "phpcs" + runs-on: "ubuntu-22.04" + + steps: + - name: "Checkout" + uses: "actions/checkout@v3" + + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ env.PHP_VERSION }} + extensions: "mongodb-${{ env.DRIVER_VERSION }}" + key: "extcache-v1" + + - name: Cache extensions + uses: actions/cache@v3 + with: + path: ${{ steps.extcache.outputs.dir }} + key: ${{ steps.extcache.outputs.key }} + restore-keys: ${{ steps.extcache.outputs.key }} + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + extensions: "mongodb-${{ env.DRIVER_VERSION }}" + php-version: "${{ env.PHP_VERSION }}" + tools: "cs2pr" + + - name: "Show driver information" + run: "php --ri mongodb" + + - name: "Install dependencies with Composer" + uses: "ramsey/composer-install@2.2.0" + with: + composer-options: "--no-suggest" + + # The -q option is required until phpcs v4 is released + - name: "Run PHP_CodeSniffer" + run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..a7a1413a8 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,60 @@ +name: "Docs" + +on: + pull_request: + branches: + - "v*.*" + - "master" + - "feature/*" + paths: + - "docs/**" + push: + branches: + - "v*.*" + - "master" + - "feature/*" + paths: + - "docs/**" + +jobs: + giza: + name: "Build Docs" + runs-on: "ubuntu-20.04" + + steps: + - name: "Checkout library" + uses: "actions/checkout@v3" + with: + path: library + fetch-depth: 2 + + - name: "Checkout docs" + uses: "actions/checkout@v3" + with: + repository: mongodb/docs-php-library + path: docs + fetch-depth: 2 + + - name: "Setup python" + uses: actions/setup-python@v4 + with: + python-version: '2.7' + + # The requirements file installs urllib3 with an incompatible version; we replace it with one that works + - name: "Install giza" + run: | + pip install -r https://raw.githubusercontent.com/mongodb/docs-tools/master/giza/requirements.txt + pip install urllib3==1.25.2 + + - name: "Sync documentation" + run: "rsync -a library/docs/ docs/source/" + + - name: "Run Giza" + run: "giza make publish" + working-directory: docs + + - name: "Upload built documentation" + uses: actions/upload-artifact@v3 + with: + name: php-library-docs.tar.gz + path: docs/build/public/master/php-library.tar.gz diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 000000000..8f9c45019 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,67 @@ +name: "Static Analysis" + +on: + pull_request: + branches: + - "v*.*" + - "master" + - "feature/*" + paths-ignore: + - "docs/**" + push: + branches: + - "v*.*" + - "master" + - "feature/*" + paths-ignore: + - "docs/**" + +env: + PHP_VERSION: "8.2" + DRIVER_VERSION: "stable" + +jobs: + psalm: + name: "Psalm" + runs-on: "ubuntu-22.04" + + steps: + - name: "Checkout" + uses: "actions/checkout@v3" + + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ env.PHP_VERSION }} + extensions: "mongodb-${{ ENV.DRIVER_VERSION }}" + key: "extcache-v1" + + - name: Cache extensions + uses: actions/cache@v3 + with: + path: ${{ steps.extcache.outputs.dir }} + key: ${{ steps.extcache.outputs.key }} + restore-keys: ${{ steps.extcache.outputs.key }} + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + extensions: "mongodb-${{ ENV.DRIVER_VERSION }}" + php-version: "${{ env.PHP_VERSION }}" + tools: "cs2pr" + + - name: "Show driver information" + run: "php --ri mongodb" + + - name: "Install dependencies with Composer" + uses: "ramsey/composer-install@2.2.0" + with: + composer-options: "--no-suggest" + + - name: "Run Psalm" + run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)" + + - name: "Run Rector" + run: "vendor/bin/rector --ansi --dry-run" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..9ce0c8b91 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,115 @@ +name: "Tests" + +on: + pull_request: + branches: + - "v*.*" + - "master" + - "feature/*" + paths-ignore: + - "docs/**" + push: + branches: + - "v*.*" + - "master" + - "feature/*" + paths-ignore: + - "docs/**" + +jobs: + phpunit: + name: "PHPUnit tests" + runs-on: "${{ matrix.os }}" + + strategy: + fail-fast: true + matrix: + os: + - "ubuntu-20.04" + php-version: + - "7.4" + - "8.0" + - "8.1" + - "8.2" + mongodb-version: + - "4.4" + driver-version: + - "stable" + topology: + - "server" + include: + - os: "ubuntu-20.04" + php-version: "8.0" + mongodb-version: "6.0" + driver-version: "stable" + topology: "replica_set" + - os: "ubuntu-20.04" + php-version: "8.0" + mongodb-version: "6.0" + driver-version: "stable" + topology: "sharded_cluster" + - os: "ubuntu-20.04" + php-version: "8.0" + mongodb-version: "5.0" + driver-version: "stable" + topology: "server" + - os: "ubuntu-20.04" + php-version: "8.0" + mongodb-version: "4.4" + driver-version: "stable" + topology: "replica_set" + - os: "ubuntu-20.04" + php-version: "8.0" + mongodb-version: "4.4" + driver-version: "stable" + topology: "sharded_cluster" + + steps: + - name: "Checkout" + uses: "actions/checkout@v3" + with: + fetch-depth: 2 + + - id: setup-mongodb + uses: mongodb-labs/drivers-evergreen-tools@master + with: + version: ${{ matrix.mongodb-version }} + topology: ${{ matrix.topology }} + + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ matrix.php-version }} + extensions: "mongodb-${{ matrix.driver-version }}" + key: "extcache-v1" + + - name: Cache extensions + uses: actions/cache@v3 + with: + path: ${{ steps.extcache.outputs.dir }} + key: ${{ steps.extcache.outputs.key }} + restore-keys: ${{ steps.extcache.outputs.key }} + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php-version }}" + tools: "pecl" + extensions: "mongodb-${{ matrix.driver-version }}" + coverage: "none" + ini-values: "zend.assertions=1" + + - name: "Show driver information" + run: "php --ri mongodb" + + - name: "Install dependencies with Composer" + uses: "ramsey/composer-install@2.2.0" + with: + composer-options: "--no-suggest" + + - name: "Run PHPUnit" + run: "vendor/bin/simple-phpunit -v" + env: + SYMFONY_DEPRECATIONS_HELPER: 999999 + MONGODB_URI: ${{ steps.setup-mongodb.outputs.cluster-uri }} diff --git a/.gitignore b/.gitignore index ee520b98e..e8e8c841b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,7 @@ phpunit.xml .phpcs-cache phpcs.xml +# psalm +psalm.xml + mongocryptd.pid diff --git a/.phpcs/autoload.php b/.phpcs/autoload.php deleted file mode 100644 index 45e90ad16..000000000 --- a/.phpcs/autoload.php +++ /dev/null @@ -1,15 +0,0 @@ -=0.6.7,<1.0" --user `whoami` - - export SERVER_FILENAME=mongodb-linux-x86_64-${SERVER_DISTRO}-${SERVER_VERSION} - - wget -qO- https://downloads.mongodb.com/linux/${SERVER_FILENAME}.tgz | tar xz - - export PATH=${PWD}/${SERVER_FILENAME}/bin:${PATH} - - mongod --version - - mongo-orchestration --version - - export MO_PATH=`python -c 'import mongo_orchestration; from os import path; print(path.dirname(mongo_orchestration.__file__));'` - -before_script: - - mongo-orchestration start - - .travis/setup_mo.sh - - .travis/install-extension.sh - - php --ri mongodb - - composer update --no-interaction --no-progress --no-suggest --prefer-dist --prefer-stable ${COMPOSER_OPTIONS} - - ulimit -c - - ulimit -c unlimited -S - -script: - - export MONGODB_URI=`cat /tmp/uri.txt` - - echo $MONGODB_URI - - vendor/bin/simple-phpunit -v - -before_cache: - - rm -f ${HOME}/.cache/pip/log/debug.log - -after_failure: - - find . -name 'core*' -exec ${TRAVIS_BUILD_DIR}/.travis/debug-core.sh {} \; - -after_script: - - mongo-orchestration stop diff --git a/.travis/debug-core.sh b/.travis/debug-core.sh deleted file mode 100755 index 6a2a01eec..000000000 --- a/.travis/debug-core.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -if [ "${TRAVIS_OS_NAME}" = "osx" ]; then - # https://www.ics.uci.edu/~pattis/common/handouts/macmingweclipse/allexperimental/mac-gdb-install.html - echo "Cannot debug core files on macOS: ${1}" - exit 1 -fi - -PHP_BINARY=`which php` -gdb -batch -ex "bt full" -ex "quit" "${PHP_BINARY}" "${1}" diff --git a/.travis/get_uri.php b/.travis/get_uri.php deleted file mode 100644 index 9c1c42895..000000000 --- a/.travis/get_uri.php +++ /dev/null @@ -1,6 +0,0 @@ -$uriField, $uriSuffix; diff --git a/.travis/install-extension.sh b/.travis/install-extension.sh deleted file mode 100755 index 9f99f71b5..000000000 --- a/.travis/install-extension.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -INI=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini -# tpecl is a helper to compile and cache php extensions -tpecl () { - local ext_name=$1 - local ext_so=$2 - local ext_dir=$(php -r "echo ini_get('extension_dir');") - local ext_cache=~/php-ext/$(basename $ext_dir)/$ext_name - if [[ -e $ext_cache/$ext_so ]]; then - echo extension = $ext_cache/$ext_so >> $INI - else - mkdir -p $ext_cache - echo yes | pecl install -f $ext_name && - cp $ext_dir/$ext_so $ext_cache - fi -} - -if [ "x${DRIVER_BRANCH}" != "x" ] || [ "x${DRIVER_REPO}" != "x" ]; then - CLONE_REPO=${DRIVER_REPO:-https://github.com/mongodb/mongo-php-driver} - CHECKOUT_BRANCH=${DRIVER_BRANCH:-master} - - echo "Compiling driver branch ${CHECKOUT_BRANCH} from repository ${CLONE_REPO}" - - mkdir -p /tmp/compile - git clone ${CLONE_REPO} /tmp/compile/mongo-php-driver - cd /tmp/compile/mongo-php-driver - - git checkout ${CHECKOUT_BRANCH} - git submodule update --init - phpize - ./configure --enable-mongodb-developer-flags - make all -j20 > /dev/null - make install - - echo "extension=mongodb.so" >> `php --ini | grep "Scan for additional .ini files in" | sed -e "s|.*:\s*||"`/mongodb.ini -elif [ "x${DRIVER_VERSION}" != "x" ]; then - echo "Installing driver version ${DRIVER_VERSION} from PECL" - tpecl mongodb-${DRIVER_VERSION} mongodb.so -fi diff --git a/.travis/mo.sh b/.travis/mo.sh deleted file mode 100755 index 13941ddb8..000000000 --- a/.travis/mo.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/bin/bash -# Copyright 2012-2014 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. - -function eval_params { - local params=$(sed -e 's|["]|\\\"|g' $1) - echo $(eval echo \"$params\") -} - -function r { - echo $1| awk -F'/' '{print $(NF-1)}'| sed 's/standalone/servers/' -} - -function a { - echo $(cd $(dirname $1); pwd)/$(basename $1) -} - -function id { - local id_line=$(grep id $1 | head -n 1) - echo $(expr "$id_line" : '.*: *"\(.*\)" *,*') -} - -function get { - echo "GET $1 $(curl --header 'Accept: application/json' --include --silent --request GET $1)" -} - -function post { - echo "POST $1 $(curl --header 'Accept: application/json' --include --silent --request POST --data "$2" $1)" -} - -function delete { - echo "DELETE $1 $(curl --header 'Accept: application/json' --include --silent --request DELETE $1)" -} - -function code { - expr "$1" : '.*HTTP/1.[01] \([0-9]*\)' -} - -function usage { - echo "usage: $0 configurations/cluster/file.json action" - echo "cluster: servers|replica_sets|sharded_clusters" - echo "action: start|status|stop" - exit 1 -} - -SSL_FILES=$(a ./ssl-files) -BASE_URL=${MONGO_ORCHESTRATION:-'http://localhost:8889'} - -if [ $# -ne 2 ]; then usage; fi -if [ ! -f "$1" ]; then echo "configuration file '$1' not found"; exit 1; fi - -ID=$(id $1) -if [ ! "$ID" ]; then echo "id field not found in configuration file '$1'"; exit 1; fi -R=$(r $1) - -GET=$(get $BASE_URL/$R/$ID) -HTTP_CODE=$(code "$GET") -EXIT_CODE=0 - -case $2 in -start) - if [ "$HTTP_CODE" != "200" ] - then - WORKSPACE=${TRAVIS_BUILD_DIR}/orchestrations - rm -fr $WORKSPACE - mkdir $WORKSPACE - LOGPATH=$WORKSPACE - DBPATH=$WORKSPACE - POST_DATA=$(eval_params $1) - echo "DBPATH=$DBPATH" - echo "LOGPATH=$LOGPATH" - echo "POST_DATA='$POST_DATA'" - echo - POST=$(post $BASE_URL/$R "$POST_DATA") - echo "$POST" - HTTP_CODE=$(code "$POST") - if [ "$HTTP_CODE" != 200 ]; then EXIT_CODE=1; fi - else - echo "$GET" - fi - ;; -stop) - if [ "$HTTP_CODE" == "200" ] - then - DELETE=$(delete $BASE_URL/$R/$ID) - echo "$DELETE" - HTTP_CODE=$(code "$DELETE") - if [ "$HTTP_CODE" != 204 ]; then EXIT_CODE=1; fi - else - echo "$GET" - fi - ;; -status) - if [ "$HTTP_CODE" == "200" ] - then - echo "$GET" - else - echo "$GET" - EXIT_CODE=1 - fi - ;; -*) - usage - ;; -esac -exit $EXIT_CODE diff --git a/.travis/setup_mo.sh b/.travis/setup_mo.sh deleted file mode 100755 index cf8c3cea8..000000000 --- a/.travis/setup_mo.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -echo Loading MO for $DEPLOYMENT - -if [[ -z $TRAVIS_BUILD_DIR ]]; then - export TRAVIS_BUILD_DIR=`pwd`; -fi - -# Replace the default client certificate with the new one to make sure mo keeps working -cp ${TRAVIS_BUILD_DIR}/mongo-orchestration/ssl/client.pem ${MO_PATH}/lib/client.pem - -URI_FIELD="mongodb_uri" -URI_SUFFIX="" - -case $DEPLOYMENT in - SHARDED_CLUSTER) - CONFIG="sharded_clusters/cluster.json" - URI_SUFFIX="/?retryWrites=false" - ;; - SHARDED_CLUSTER_RS) - CONFIG="sharded_clusters/cluster_replset.json" - ;; - STANDALONE_AUTH) - CONFIG="standalone/standalone-auth.json" - URI_FIELD="mongodb_auth_uri" - ;; - STANDALONE_OLD) - CONFIG="standalone/standalone-old.json" - ;; - STANDALONE_SSL) - CONFIG="standalone/standalone-ssl.json" - URI_SUFFIX="/?ssl=true&sslallowinvalidcertificates=true" - ;; - REPLICASET) - CONFIG="replica_sets/replicaset.json" - ;; - REPLICASET_SINGLE) - CONFIG="replica_sets/replicaset-one-node.json" - ;; - REPLICASET_OLD) - CONFIG="replica_sets/replicaset-old.json" - ;; - *) - CONFIG="standalone/standalone.json" - ;; -esac - -${TRAVIS_BUILD_DIR}/.travis/mo.sh ${TRAVIS_BUILD_DIR}/mongo-orchestration/$CONFIG start > /tmp/mo-result.json - -if [ $? -ne 0 ]; then - cat /tmp/mo-result.json - cat ${TRAVIS_BUILD_DIR}/server.log - exit 1 -fi - -cat /tmp/mo-result.json | tail -n 1 | php ${TRAVIS_BUILD_DIR}/.travis/get_uri.php $URI_FIELD $URI_SUFFIX > /tmp/uri.txt - -echo -n "MongoDB Test URI: " -cat /tmp/uri.txt -echo - -echo "Raw MO Response:" -cat /tmp/mo-result.json - -echo diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7f1105f1b..7e9e46404 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,7 @@ $ composer update In addition to installing project dependencies, Composer will check that the required extension version is installed. Directions for installing the extension -may be found [here](http://php.net/manual/en/mongodb.installation.php). +may be found [here](https://php.net/manual/en/mongodb.installation.php). Installation directions for Composer may be found in its [Getting Started](https://getcomposer.org/doc/00-intro.md) guide. @@ -31,44 +31,137 @@ $ vendor/bin/simple-phpunit ``` The `phpunit.xml.dist` file is used as the default configuration file for the -test suite. In addition to various PHPUnit options, it defines required -`MONGODB_URI` and `MONGODB_DATABASE` environment variables. You may customize +test suite. In addition to various PHPUnit options, it defines environment +variables such as `MONGODB_URI` and `MONGODB_DATABASE`. You may customize this configuration by creating your own `phpunit.xml` file based on the -`phpunit.xml.dist` file we provide. +`phpunit.xml.dist` file we provide. To run the tests in serverless mode, set the +`MONGODB_IS_SERVERLESS` environment variable to `on`. + +To run tests against a cluster that requires authentication, either include the +credentials in the connection string (i.e. `MONGODB_URI`) or set the +`MONGODB_USERNAME` and `MONGODB_PASSWORD` environment variables accordingly. +Note that `MONGODB_USERNAME` and `MONGODB_PASSWORD` will override any +credentials present in the connection string. By default, the `simple-phpunit` binary chooses the correct PHPUnit version for -the PHP version you are running. To run tests against a specific PHPUnit version, -use the `SYMFONY_PHPUNIT_VERSION` environment variable: +the PHP version you are running. To run tests against a specific PHPUnit +version, use the `SYMFONY_PHPUNIT_VERSION` environment variable: ``` $ SYMFONY_PHPUNIT_VERSION=7.5 vendor/bin/simple-phpunit ``` +### Environment Variables + +The test suite references the following environment variables: + + * `MONGODB_DATABASE`: Default database to use in tests. Defaults to + `phplib_test`. + * `MONGODB_PASSWORD`: If specified, this value will be appended as the + `password` URI option for clients constructed by the test suite, which will + override any credentials in the connection string itself. + * `MONGODB_URI`: Connection string. Defaults to `mongodb://127.0.0.1/`, which + assumes a MongoDB server is listening on localhost port 27017. + * `MONGODB_USERNAME`: If specified, this value will be appended as the + `username` URI option for clients constructed by the test suite, which will + override any credentials in the connection string itself. + +The following environment variable is used for [stable API testing](https://github.com/mongodb/specifications/blob/master/source/versioned-api/tests/README.rst): + + * `API_VERSION`: If defined, this value will be used to construct a + [`MongoDB\Driver\ServerApi`](https://www.php.net/manual/en/mongodb-driver-serverapi.construct.php), + which will then be specified as the `serverApi` driver option for clients + created by the test suite. + +The following environment variable is used for [serverless testing](https://github.com/mongodb/specifications/blob/master/source/serverless-testing/README.rst): + + * `MONGODB_IS_SERVERLESS`: Specify a true boolean string + (see: [`FILTER_VALIDATE_BOOLEAN`](https://www.php.net/manual/en/filter.filters.validate.php)) + if `MONGODB_URI` points to a serverless instance. Defaults to false. + +The following environment variables are used for [load balancer testing](https://github.com/mongodb/specifications/blob/master/source/load-balancers/tests/README.rst): + + * `MONGODB_SINGLE_MONGOS_LB_URI`: Connection string to a load balancer backed + by a single mongos host. + * `MONGODB_MULTI_MONGOS_LB_URI`: Connection string to a load balancer backed by + multiple mongos hosts. + +The following environment variables are used for [CSFLE testing](https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst): + + * `AWS_ACCESS_KEY_ID` + * `AWS_SECRET_ACCESS_KEY` + * `AWS_TEMP_ACCESS_KEY_ID` + * `AWS_TEMP_SECRET_ACCESS_KEY` + * `AWS_TEMP_SESSION_TOKEN` + * `AZURE_TENANT_ID` + * `AZURE_CLIENT_ID` + * `AZURE_CLIENT_SECRET` + * `CRYPT_SHARED_LIB_PATH`: If defined, this value will be used to set the + `cryptSharedLibPath` autoEncryption driver option for clients created by the + test suite. + * `GCP_EMAIL` + * `GCP_PRIVATE_KEY` + * `KMIP_ENDPOINT` + * `KMS_ENDPOINT_EXPIRED` + * `KMS_ENDPOINT_WRONG_HOST` + * `KMS_ENDPOINT_REQUIRE_CLIENT_CERT` + * `KMS_TLS_CA_FILE` + * `KMS_TLS_CERTIFICATE_KEY_FILE` + ## Checking coding standards The library's code is checked using [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer), -which is installed as a development dependency by Composer. Due to the PHP -requirement, the base version of the coding standard is not installed and needs -to be added manually if you plan to contributing code: +which is installed as a development dependency by Composer. To check the code +for style errors, run the `phpcs` binary: + + +``` +$ vendor/bin/phpcs +``` + +To automatically fix all fixable errors, use the `phpcbf` binary: ``` -$ composer require --dev doctrine/coding-standard=^6.0 +$ vendor/bin/phpcbf ``` -Once the coding standard has been installed, you can check the code for style -errors: +## Running static analysis +The library uses [psalm](https://psalm.dev) to run static analysis on the code +and ensure an additional level of type safety. New code is expected to adhere +to level 1, with a baseline covering existing issues. To run static analysis +checks, run the `psalm` binary: ``` -$ vendor/bin/phpcs +$ vendor/bin/psalm ``` -To automatically fix all fixable errors, use the `phpcbf` binary: +To remove fixed errors from the baseline, you can use the `update-baseline` +command-line argument: ``` -$ vendor/bin/phpcbf +$ vendor/bin/psalm --update-baseline ``` +Note that this will not add new errors to the baseline. New errors should be +fixed instead of being added to the technical debt, but in case this isn't +possible it can be added to the baseline using `set-baseline`: + +``` +$ vendor/bin/psalm --set-baseline=psalm-baseline.xml +``` + +## Automatic code refactoring + +The library uses [rector](https://getrector.com/) to refactor the code for new features. +To run automatic refactoring, use the `rector` command: + +``` +$ vendor/bin/rector +``` + +New rules can be added to the `rector.php` configuration file. + ## Documentation Documentation for the library lives in the `docs/` directory and is built with @@ -83,18 +176,65 @@ repository: * Clone the [mongodb/docs-php-library](https://github.com/mongodb/docs-php-library) tools repository. - * Install [giza](https://pypi.python.org/pypi/giza/), as noted in the tools - README. + * Create and activate Python 2.7 virtual environment if necessary. + + ``` + $ virtualenv -p python2.7 venv + $ source venv/bin/activate + ``` + * Install [giza](https://pypi.python.org/pypi/giza/) according to the instructions + in the [mongodb/docs-tools](https://github.com/mongodb/docs-tools) README. * Sync your working copy of the documentation to the `source/` directory with `rsync -a --delete /path/to/mongo-php-library/docs/ source/`. * Build the documentation with `giza make publish`. You can suppress informational log messages with the `--level warning` option. * Generated documentation may be found in the `build/master/html` directory. +## Creating a maintenance branch and updating master branch alias + +After releasing a new major or minor version (e.g. 1.9.0), a maintenance branch +(e.g. v1.9) should be created. Any development towards a patch release (e.g. +1.9.1) would then be done within that branch and any development for the next +major or minor release can continue in master. + +After creating a maintenance branch, the `extra.branch-alias.dev-master` field +in the master branch's `composer.json` file should be updated. For example, +after branching v1.9, `composer.json` in the master branch may still read: + +``` +"branch-alias": { + "dev-master": "1.9.x-dev" +} +``` + +The above would be changed to: + +``` +"branch-alias": { + "dev-master": "1.10.x-dev" +} +``` + +Commit this change: + +``` +$ git commit -m "Master is now 1.10-dev" composer.json +``` + ## Releasing -The follow steps outline the release process for a maintenance branch (e.g. -releasing the `vX.Y` branch as X.Y.Z). +The following steps outline the release process for both new minor versions (e.g. +releasing the `master` branch as X.Y.0) and patch versions (e.g. releasing the +`vX.Y` branch as X.Y.Z). + +The command examples below assume that the canonical "mongodb" repository has +the remote name "mongodb". You may need to adjust these commands if you've given +the remote another name (e.g. "upstream"). The "origin" remote name was not used +as it likely refers to your personal fork. + +It helps to keep your own fork in sync with the "mongodb" repository (i.e. any +branches and tags on the main repository should also exist in your fork). This +is left as an exercise to the reader. ### Ensure PHP version compatibility @@ -116,7 +256,7 @@ page. ### Update version info -The PHP library uses [semantic versioning](http://semver.org/). Do not break +The PHP library uses [semantic versioning](https://semver.org/). Do not break backwards compatibility in a non-major release or your users will kill you. Before proceeding, ensure that the `master` branch is up-to-date with all code @@ -124,63 +264,126 @@ changes in this maintenance branch. This is important because we will later merge the ensuing release commits up to master with `--strategy=ours`, which will ignore changes from the merged commits. -### Tag release +### Update composer.json and CI matrices + +This is especially important before releasing a new minor version. + +Ensure that the extension requirement and branch alias in `composer.json` are +correct for the library version being released. For example, the 1.15.0 release +of the library should depend on version `^1.15.0` of the extension and master +branch alias should be `1.15.x-dev`. + +If this is the first release of a minor version for the library, it is likely +following an extension release. In that case, the `driver-versions` matrix in +the Evergreen configuration should be updated: + +```diff + - id: "oldest-supported" +- display_name: "PHPC 1.15-dev" ++ display_name: "PHPC 1.15.0" + variables: +- EXTENSION_BRANCH: "master" ++ EXTENSION_VERSION: "1.15.0" + - id: "latest-stable" +- display_name: "PHPC 1.15-dev" ++ display_name: "PHPC 1.15.x" + variables: +- EXTENSION_BRANCH: "master" ++ EXTENSION_VERSION: "stable" + - id: "latest-dev" +- display_name: "PHPC 1.15-dev" ++ display_name: "PHPC 1.16-dev" + variables: + EXTENSION_BRANCH: "master" +``` -The maintenance branch's HEAD will be the target for our release tag: +Commit and push any changes: + +``` +$ git commit -m "Update composer.json and CI matrices for X.Y.Z" composer.json .evergreen/config.yml +$ git push mongodb +``` + +### Tag the release + +Create a tag for the release and push: ``` $ git tag -a -m "Release X.Y.Z" X.Y.Z +$ git push mongodb --tags +``` + +### Branch management + +#### After releasing a new minor version + +After a new minor version is released (i.e. `master` was tagged), a maintenance +branch should be created for future patch releases: + +``` +$ git checkout -b vX.Y +$ git push mongodb vX.Y +``` + +Update the master branch alias in `composer.json`: + +```diff + "extra": { + "branch-alias": { +- "dev-master": "1.15.x-dev" ++ "dev-master": "1.16.x-dev" + } + }, ``` -### Push tags +Commit and push this change: ``` -$ git push --tags +$ git commit -m "Master is now X.Y-dev" composer.json +$ git push mongodb ``` -### Merge the maintenance branch up to master +#### After releasing a patch version + +If this was a patch release, the maintenance branch must be merged up to master: ``` $ git checkout master +$ git pull mongodb master $ git merge vX.Y --strategy=ours -$ git push +$ git push mongodb ``` The `--strategy=ours` option ensures that all changes from the merged commits -will be ignored. +will be ignored. This is OK because we previously ensured that the `master` +branch was up-to-date with all code changes in this maintenance branch before +tagging. ### Publish release notes The following template should be used for creating GitHub release notes via [this form](https://github.com/mongodb/mongo-php-library/releases/new). -``` -The PHP team is happy to announce that version X.Y.Z of the MongoDB PHP library is now available. This library is a high-level abstraction for the [`mongodb`](http://php.net/mongodb) extension. +```markdown +The PHP team is happy to announce that version X.Y.Z of the MongoDB PHP library is now available. **Release Highlights** -A complete list of resolved issues in this release may be found at: -$JIRA_URL +A complete list of resolved issues in this release may be found in [JIRA]($JIRA_URL). **Documentation** -Documentation for this library may be found at: -https://docs.mongodb.com/php-library/ - -**Feedback** - -If you encounter any bugs or issues with this library, please report them via this form: -https://jira.mongodb.org/secure/CreateIssue.jspa?pid=12483&issuetype=1 +Documentation for this library may be found in the [PHP Library Manual](https://mongodb.com/docs/php-library/current/). **Installation** This library may be installed or upgraded with: - composer require mongodb/mongodb + composer require mongodb/mongodb:X.Y.Z -Installation instructions for the `mongodb` extension may be found in the [PHP.net documentation](http://php.net/manual/en/mongodb.installation.php). +Installation instructions for the `mongodb` extension may be found in the [PHP.net documentation](https://php.net/manual/en/mongodb.installation.php). ``` The URL for the list of resolved JIRA issues will need to be updated with each @@ -190,7 +393,7 @@ release. You may obtain the list from If commits from community contributors were included in this release, append the following section: -``` +```markdown **Thanks** Thanks for our community contributors for this release: @@ -198,13 +401,23 @@ Thanks for our community contributors for this release: * [$CONTRIBUTOR_NAME](https://github.com/$GITHUB_USERNAME) ``` -Release announcements should also be sent to the [MongoDB Product & Driver Announcements](https://community.mongodb.com/tags/c/community/release-notes/35/php-driver). +Release announcements should also be posted in the [MongoDB Product & Driver Announcements: Driver Releases](https://mongodb.com/community/forums/tags/c/announcements/driver-releases/110/php) forum and shared on Twitter. + +### Documentation Updates for New Major and Minor Versions + +New major and minor releases will also require documentation updates to other +projects: -Consider announcing each release on Twitter. Significant releases should also be -announced via [@MongoDB](http://twitter.com/mongodb) as well. + * Create a DOCSP ticket to add the new version to PHP's server and language + [compatibility tables](https://mongodb.com/docs/drivers/php/#compatibility) + in the driver docs portal. See + [mongodb/docs-ecosystem#642](https://github.com/mongodb/docs-ecosystem/pull/642) + for an example. -### Update compatibility tables in MongoDB docs + * Create a DOCSP ticket to update the "current" and "upcoming" navigation links + in the library's [documentation](https://mongodb.com/docs/php-library/current). This + will require updating + [mongodb/docs-php-library](https://github.com/mongodb/docs-php-library). -The [compatibility tables](https://docs.mongodb.com/drivers/driver-compatibility-reference#php-driver-compatibility) in -the MongoDB documentation must be updated to account for new releases. Make sure to update both MongoDB and Language -compatibility tables, as shown in [this pull request](https://github.com/mongodb/docs-ecosystem/pull/642). +These tasks can be initiated prior to tagging a new release to ensure that the +updated content becomes accessible soon after the release is published. diff --git a/Makefile b/Makefile deleted file mode 100644 index a1d31b68f..000000000 --- a/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: composer test - -COMPOSER_ARGS=update --no-interaction --prefer-source - -composer: - @command -v composer >/dev/null 2>&1; \ - if test $$? -eq 0; then \ - composer $(COMPOSER_ARGS); \ - elif test -r composer.phar; then \ - php composer.phar $(COMPOSER_ARGS); \ - else \ - echo >&2 "Cannot find composer; aborting."; \ - false; \ - fi - -test: composer - vendor/bin/phpunit diff --git a/README.md b/README.md index cc8bdd9af..8b0fbf3da 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ # MongoDB PHP Library -[![Build Status](https://api.travis-ci.org/mongodb/mongo-php-library.png?branch=master)](https://travis-ci.org/mongodb/mongo-php-library) +![Tests](https://github.com/mongodb/mongo-php-library/workflows/Tests/badge.svg) +![Coding Standards](https://github.com/mongodb/mongo-php-library/workflows/Coding%20Standards/badge.svg) This library provides a high-level abstraction around the lower-level [PHP driver](https://github.com/mongodb/mongo-php-driver) (`mongodb` extension). While the extension provides a limited API for executing commands, queries, and -write operations, this library implements an API similar to that of the -[legacy PHP driver](https://php.net/manual/en/book.mongo.php). It contains -abstractions for client, database, and collection objects, and provides methods -for CRUD operations and common commands (e.g. index and collection management). +write operations, this library implements a full-featured API similar to that of +other MongoDB drivers. It contains abstractions for client, database, and +collection objects, and provides methods for CRUD operations and common commands +(e.g. index and collection management). If you are developing an application with MongoDB, you should consider using this library, or another high-level abstraction, instead of the extension alone. @@ -20,8 +21,8 @@ extension may be found in ## Documentation - - https://docs.mongodb.com/php-library/ - - https://docs.mongodb.com/ecosystem/drivers/php/ + - https://mongodb.com/docs/php-library/current + - https://mongodb.com/docs/ecosystem/drivers/php/ ## Installation @@ -32,7 +33,7 @@ root: $ composer require mongodb/mongodb Additional installation instructions may be found in the -[library documentation](https://docs.mongodb.com/php-library/current/tutorial/install-php-library/). +[library documentation](https://mongodb.com/docs/php-library/current/tutorial/install-php-library/). Since this library is a high-level abstraction for the driver, it also requires that the `mongodb` extension be installed: @@ -52,13 +53,13 @@ project in MongoDB's JIRA. Extension-related issues should be reported in the project. For general questions and support requests, please use one of MongoDB's -[Technical Support](https://docs.mongodb.com/manual/support/) channels. +[Technical Support](https://mongodb.com/docs/manual/support/) channels. ### Security Vulnerabilities If you've identified a security vulnerability in a driver or any other MongoDB project, please report it according to the instructions in -[Create a Vulnerability Report](https://docs.mongodb.org/manual/tutorial/create-a-vulnerability-report). +[Create a Vulnerability Report](https://mongodb.com/docs/manual/tutorial/create-a-vulnerability-report). ## Development diff --git a/UPGRADE-1.15.md b/UPGRADE-1.15.md new file mode 100644 index 000000000..d836ce3f1 --- /dev/null +++ b/UPGRADE-1.15.md @@ -0,0 +1,66 @@ +# UPGRADE FROM 1.x to 1.15 + +## Method signature changes + +### Parameter types + +Starting with 1.15, methods now declare types for their arguments. This will not +cause BC breaks unless you've passed a type that was incompatible with the type +previously documented in the PHPDoc comment. A list of changes can be found at +the bottom of this document. + +### Return types + +Return types will be added in version 2.0. These types are documented in a +PHPDoc comment and will eventually become a declared return type. You can +prepare for this change (which will trigger a BC break in any class you may +extend) by adding the correct return type to your class at this time. + +## Internal classes + +Internal classes (i.e. annotated with `@internal`) will become final where +possible in a future release. At the same time, we will add return types to +these internal classes. Note that internal classes are not covered by our +backward compatibility promise, and you should not instantiate such classes +directly. + +## Method signature changes by class + +### MongoDB\Client + +| 1.13 | 1.15 | +|-----------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------| +| `__construct($uri = 'mongodb://127.0.0.1', array $uriOptions = [], array $driverOptions = [])` | `__construct(?string $uri = null, array $uriOptions = [], array $driverOptions = [])` | +| `__get($databaseName)` | `__get(string $databaseName)` | +| `dropDatabase($databaseName, array $options = [])` | `dropDatabase(string $databaseName, array $options = [])` | +| `selectCollection($databaseName, $collectionName, array $options = [])` | `selectCollection(string $databaseName, string $collectionName, array $options = [])` | +| `selectDatabase($databaseName, array $options = [])` | `selectDatabase(string $databaseName, array $options = [])` | + +### MongoDB\Database + +| 1.13 | 1.15 | +|-----------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------| +| `__construct(MongoDB\Driver\Manager $manager, $databaseName, array $options = [])` | `__construct(MongoDB\Driver\Manager $manager, string $databaseName, array $options = [])` | +| `__get($collectionName)` | `__get(string $collectionName)` | +| `createCollection($collectionName, array $options = [])` | `createCollection(string $collectionName, array $options = [])` | +| `dropCollection($collectionName, array $options = [])` | `dropCollection(string $collectionName, array $options = [])` | +| `modifyCollection($collectionName, array $collectionOptions, array $options = [])` | `modifyCollection(string $collectionName, array $collectionOptions, array $options = [])` | +| `selectCollection($collectionName, array $options = [])` | `selectCollection(string $collectionName, array $options = [])` | + +### MongoDB\Collection + +| 1.13 | 1.15 | +|----------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------| +| `__construct(MongoDB\Driver\Manager $manager, $databaseName, $collectionName, array $options = [])` | `__construct(MongoDB\Driver\Manager $manager, string $databaseName, string $collectionName, array $options = [])` | +| `distinct($fieldName, $filter = [], array $options = [])` | `distinct(string $fieldName, $filter = [], array $options = [])` | + +### MongoDB\GridFS\Bucket + +| 1.13 | 1.15 | +|-----------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------| +| `__construct(MongoDB\Driver\Manager $manager, $databaseName, array $options = [])` | `__construct(MongoDB\Driver\Manager $manager, string $databaseName, array $options = [])` | +| `downloadToStreamByName($filename, $destination, array $options = [])` | `downloadToStreamByName(string $filename, $destination, array $options = [])` | +| `openDownloadStreamByName($filename, array $options = [])` | `openDownloadStreamByName(string $filename, array $options = [])` | +| `openUploadStream($filename, array $options = [])` | `openUploadStream(string $filename, array $options = [])` | +| `uploadFromStream($filename, $source, array $options = [])` | `uploadFromStream(string $filename, $source, array $options = [])` | +| `rename($id, $newFilename)` | `rename($id, string $newFilename)` | diff --git a/composer.json b/composer.json index c6e282228..fe9698d0e 100644 --- a/composer.json +++ b/composer.json @@ -9,16 +9,21 @@ { "name": "Jeremy Mikola", "email": "jmikola@gmail.com" } ], "require": { - "php": "^7.0 || ^8.0", + "php": "^7.2 || ^8.0", "ext-hash": "*", "ext-json": "*", - "ext-mongodb": "^1.8.1", - "jean85/pretty-package-versions": "^1.2", - "symfony/polyfill-php80": "^1.19" + "ext-mongodb": "^1.16.0", + "jean85/pretty-package-versions": "^2.0.1", + "symfony/polyfill-php73": "^1.27", + "symfony/polyfill-php80": "^1.27", + "symfony/polyfill-php81": "^1.27" }, "require-dev": { - "squizlabs/php_codesniffer": "^3.5, <3.5.5", - "symfony/phpunit-bridge": "5.x-dev" + "doctrine/coding-standard": "^11.1", + "rector/rector": "^0.16.0", + "squizlabs/php_codesniffer": "^3.7", + "symfony/phpunit-bridge": "^5.2", + "vimeo/psalm": "^4.28" }, "autoload": { "psr-4": { "MongoDB\\": "src/" }, @@ -30,7 +35,13 @@ }, "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.16.x-dev" } + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + }, + "sort-packages": true } } diff --git a/docs/faq.txt b/docs/faq.txt new file mode 100644 index 000000000..9cdd9a250 --- /dev/null +++ b/docs/faq.txt @@ -0,0 +1,182 @@ +========================== +Frequently Asked Questions +========================== + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Common Extension Installation Errors +------------------------------------ + +PHP Headers Not Found +~~~~~~~~~~~~~~~~~~~~~ + +For example: + +.. code-block:: none + + /private/tmp/pear/install/mongodb/php_phongo.c:24:10: fatal error: 'php.h' file not found + + #include + ^~~~~~~ + +This error indicates that PHP's build system cannot find the necessary headers. +All PHP extensions require headers in order to compile. Additionally, those +headers must correspond to the PHP runtime for which the extension will be used. +Generally, the ``phpize`` command (invoked by ``pecl``) will ensure that the +extension builds with the correct headers. + +Note that the mere presence of a PHP runtime does not mean that headers are +available. On various Linux distributions, headers are often published under a +separate ``php-dev`` or ``php-devel`` package. On macOS, the default PHP runtime +does not include headers and users typically need to install PHP (and headers) +via `Homebrew `_ in order to build an extension. + +Multiple PHP Runtimes Installed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your system has multiple versions of PHP installed, each version will have +its own ``pecl`` and ``phpize`` commands. Additionally, each PHP runtime may +have separate ``php.ini`` files for each SAPI (e.g. FPM, CLI). If the extension +has been installed but is not available at runtime, double-check that you have +used the correct ``pecl`` command and have modified the appropriate ``php.ini`` +file(s). + +If there is any doubt about the ``php.ini`` file being used by a PHP runtime, +you should examine the output of :php:`phpinfo() ` for that particular +SAPI. Additionally, :php:`php_ini_loaded_file() ` and +:php:`php_ini_scanned_files() ` may be used to determine +exactly which INI files have been loaded by PHP. + +To debug issues with the extension not being loaded, you can use the +``detect-extension`` script provided in the tools directory. You can run this +script from the CLI or include it in a script accessible via your web server. +The tool will point out potential issues and installation instructions for your +system. Assuming you've installed the library through Composer, you can call the +script from the vendor directory: + +.. code-block:: none + + php vendor/mongodb/mongodb/tools/detect-extension.php + +If you want to check configuration for a web server SAPI, include the file in +a script accessible from the web server and open it in your browser. Remember to +wrap the script in ``
`` tags to properly format its output:
+
+.. code-block:: php
+
+   
+ +Loading an Incompatible DLL on Windows +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Windows binaries are available for various combinations of PHP version, +thread safety (TS or NTS), and architecture (x86 or x64). Failure to select the +correct binary will result in an error when attempting to load the extension DLL +at runtime: + +.. code-block:: none + + PHP Warning: PHP Startup: Unable to load dynamic library 'mongodb' + +Ensure that you have downloaded a DLL that corresponds to the following PHP +runtime properties: + +- PHP version (``PHP_VERSION``) +- Thread safety (``PHP_ZTS``) +- Architecture (``PHP_INT_SIZE``) + +In addition to the aforementioned constants, these properties can also be +inferred from :php:`phpinfo() `. If your system has multiple PHP +runtimes installed, double-check that you are examining the ``phpinfo()`` output +for the correct environment. + +The aforementioned ``detect-extension`` script can also be used to determine the +appropriate DLL for your PHP environment. + +Server Selection Failures +------------------------- + +The following are all examples of +:doc:`Server Selection ` failures: + +.. code-block:: none + + No suitable servers found (`serverSelectionTryOnce` set): + [connection refused calling hello on 'a.example.com:27017'] + [connection refused calling hello on 'b.example.com:27017'] + + No suitable servers found: `serverSelectionTimeoutMS` expired: + [socket timeout calling hello on 'example.com:27017'] + + No suitable servers found: `serverSelectionTimeoutMS` expired: + [connection timeout calling hello on 'a.example.com:27017'] + [connection timeout calling hello on 'b.example.com:27017'] + [TLS handshake failed: -9806 calling hello on 'c.example.com:27017'] + + No suitable servers found: `serverselectiontimeoutms` timed out: + [TLS handshake failed: certificate verify failed (64): IP address mismatch calling hello on 'a.example.com:27017'] + [TLS handshake failed: certificate verify failed (64): IP address mismatch calling hello on 'b.example.com:27017'] + +These errors typically manifest as a +:php:`MongoDB\\Driver\\Exception\\ConnectionTimeoutException ` +exception from the driver. The actual exception messages originate from +libmongoc, which is the underlying library used by the PHP driver. Since these +messages can take many forms, it's helpful to break down the structure of the +message so you can better diagnose errors in your application. + +Messages will typically start with "No suitable servers found". The next part of +the message indicates *how* server selection failed. By default, the PHP driver +avoids a server selection loop and instead makes a single attempt (according to +the ``serverSelectionTryOnce`` connection string option). If the driver is +configured to utilize a loop, a message like "serverSelectionTimeoutMS expired" +will tell us that we exhausted its time limit. + +The last component of the message tells us *why* server selection failed, and +includes one or more errors directly from the topology scanner, which is the +service responsible for connecting to and monitoring each host. Any host that +last experienced an error during monitoring will be included in this list. These +messages typically originate from low-level socket or TLS functions. + +The following is not meant to be exhaustive, but will hopefully point you in the +right direction for analyzing the contributing factor(s) for a server selection +failure: + +- "connection refused" likely indicates that the remote host is not listening on + the expected port. +- "connection timeout" could indicate a routing or firewall issue, or perhaps + a timeout due to latency. +- "socket timeout" suggests that a connection *was* established at some point + but was dropped or otherwise timed out due to latency. +- "TLS handshake failed" suggests something related to TLS or OCSP verification + and is sometimes indicative of misconfigured TLS certificates. + +In the case of a connection failure, you can use the ``connect`` tool to try and +receive more information. This tool attempts to connect to each host in a +connection string using socket functions to see if it is able to establish a +connection, send, and receive data. The tool takes the connection string to a +MongoDB deployment as its only argument. Assuming you've installed the library +through Composer, you would call the script from the vendor directory: + +.. code-block:: none + + php vendor/mongodb/mongodb/tools/connect.php mongodb://127.0.0.1:27017 + +In case the server does not accept connections, the output will look like this: + +.. code-block:: none + + Looking up MongoDB at mongodb://127.0.0.1:27017 + Found 1 host(s) in the URI. Will attempt to connect to each. + + Could not connect to 127.0.0.1:27017: Connection refused + +.. note:: + + The tool only supports the ``mongodb://`` URI schema. Using the + ``mongodb+srv`` scheme is not supported. diff --git a/docs/includes/apiargs-MongoDBClient-common-option.yaml b/docs/includes/apiargs-MongoDBClient-common-option.yaml index 89092358f..4ffc52a92 100644 --- a/docs/includes/apiargs-MongoDBClient-common-option.yaml +++ b/docs/includes/apiargs-MongoDBClient-common-option.yaml @@ -4,9 +4,6 @@ type: :php:`MongoDB\\Driver\\ReadConcern ` description: | :manual:`Read concern ` to use for the operation. Defaults to the client's read concern. - - This is not supported for server versions prior to 3.2 and will result in an - exception at execution time if used. interface: phpmethod operation: ~ optional: true diff --git a/docs/includes/apiargs-MongoDBClient-method-construct-driverOptions.yaml b/docs/includes/apiargs-MongoDBClient-method-construct-driverOptions.yaml index 77520c0d2..8406bd091 100644 --- a/docs/includes/apiargs-MongoDBClient-method-construct-driverOptions.yaml +++ b/docs/includes/apiargs-MongoDBClient-method-construct-driverOptions.yaml @@ -1,4 +1,59 @@ arg_name: option +name: autoEncryption +type: array +description: | + Options to configure client-side field-level encryption in the driver. The + encryption options are documented in the :php:`extension documentation + `. + For the ``keyVaultClient`` option, you may pass a :phpclass:`MongoDB\\Client` + instance, which will be unwrapped to provide a :php:`MongoDB\\Driver\\Manager ` + to the extension. + + .. versionadded:: 1.6 +interface: phpmethod +operation: ~ +optional: true +--- +arg_name: option +name: driver +type: array +description: | + Additional driver metadata to be passed on to the server handshake. This is an + array containing ``name``, ``version``, and ``platform`` fields: + + .. code-block:: php + + [ + 'name' => 'my-driver', + 'version' => '1.2.3-dev', + 'platform' => 'some-platform', + ] + + .. note:: + + This feature is primarily designed for custom drivers and ODMs, which may + want to identify themselves to the server for diagnostic purposes. + Applications should use the ``appName`` URI option instead of driver + metadata. + + .. versionadded:: 1.7 +interface: phpmethod +operation: ~ +optional: true +--- +arg_name: option +name: serverApi +type: :php:`MongoDB\\Driver\\ServerApi ` +description: | + Used to declare an API version on the client. See the + :manual:`Stable API tutorial ` for usage. + + .. versionadded:: 1.9 +interface: phpmethod +operation: ~ +optional: true +--- +arg_name: option name: typeMap type: array description: | @@ -131,46 +186,4 @@ description: | interface: phpmethod operation: ~ optional: true ---- -arg_name: option -name: autoEncryption -type: array -description: | - Options to configure client-side field-level encryption in the driver. The - encryption options are documented in the :php:`extension documentation - `. - For the ``keyVaultClient`` option, you may pass a :phpclass:`MongoDB\\Client` - instance, which will be unwrapped to provide a :php:`MongoDB\\Driver\\Manager ` - to the extension. - .. versionadded:: 1.6 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: driver -type: array -description: | - Additional driver metadata to be passed on to the server handshake. This is an - array containing ``name``, ``version``, and ``platform`` fields: - - .. code-block:: php - - [ - 'name' => 'my-driver', - 'version' => '1.2.3-dev', - 'platform' => 'some-platform', - ] - - .. note:: - - This feature is primarily designed for custom drivers and ODMs, which may - want to identify themselves to the server for diagnostic purposes. - Applications should use the ``appName`` URI option instead of driver - metadata. - - .. versionadded:: 1.7 -interface: phpmethod -operation: ~ -optional: true ... diff --git a/docs/includes/apiargs-MongoDBClient-method-construct-param.yaml b/docs/includes/apiargs-MongoDBClient-method-construct-param.yaml index 4b6ff4205..62998287e 100644 --- a/docs/includes/apiargs-MongoDBClient-method-construct-param.yaml +++ b/docs/includes/apiargs-MongoDBClient-method-construct-param.yaml @@ -9,7 +9,7 @@ description: | Defaults to ``"mongodb://127.0.0.1:27017"`` if unspecified. Any special characters in the URI components need to be encoded according to - `RFC 3986 `_. This is particularly + `RFC 3986 `_. This is particularly relevant to the username and password, which can often include special characters such as ``@``, ``:``, or ``%``. When connecting via a Unix domain socket, the socket path may contain special characters such as slashes and @@ -26,7 +26,7 @@ description: | Specifies additional URI options, such as authentication credentials or query string parameters. The options specified in ``$uriOptions`` take precedence over any analogous options present in the ``$uri`` string and do not need to - be encoded according to `RFC 3986 `_. + be encoded according to `RFC 3986 `_. Refer to the :php:`MongoDB\\Driver\\Manager::__construct() ` extension reference and :manual:`MongoDB diff --git a/docs/includes/apiargs-MongoDBClient-method-dropDatabase-option.yaml b/docs/includes/apiargs-MongoDBClient-method-dropDatabase-option.yaml index e219c1459..be0a19aa9 100644 --- a/docs/includes/apiargs-MongoDBClient-method-dropDatabase-option.yaml +++ b/docs/includes/apiargs-MongoDBClient-method-dropDatabase-option.yaml @@ -1,3 +1,12 @@ +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: session @@ -13,7 +22,4 @@ post: | source: file: apiargs-MongoDBClient-common-option.yaml ref: writeConcern -post: | - This is not supported for server versions prior to 3.4 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBClient-method-listDatabases-option.yaml b/docs/includes/apiargs-MongoDBClient-method-listDatabases-option.yaml index 4e3d616d8..38baf93ad 100644 --- a/docs/includes/apiargs-MongoDBClient-method-listDatabases-option.yaml +++ b/docs/includes/apiargs-MongoDBClient-method-listDatabases-option.yaml @@ -4,7 +4,7 @@ type: boolean description: | A flag that determines which databases are returned based on the user privileges when access control is enabled. For more information, see the - `listDatabases command documentation `_. + `listDatabases command documentation `_. For servers < 4.0.5, this option is ignored. @@ -13,6 +13,15 @@ interface: phpmethod operation: ~ optional: true --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- arg_name: option name: filter type: array|object @@ -36,4 +45,3 @@ source: ref: session post: | .. versionadded:: 1.3 -... diff --git a/docs/includes/apiargs-MongoDBClient-method-watch-option.yaml b/docs/includes/apiargs-MongoDBClient-method-watch-option.yaml index 2dd040e95..cad9c3f75 100644 --- a/docs/includes/apiargs-MongoDBClient-method-watch-option.yaml +++ b/docs/includes/apiargs-MongoDBClient-method-watch-option.yaml @@ -7,10 +7,25 @@ source: file: apiargs-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + The comment can be any valid BSON type for server versions 4.4 and above. + Earlier server versions only support string values. + + .. versionadded:: 1.13 +--- source: file: apiargs-method-watch-option.yaml ref: fullDocument --- +source: + file: apiargs-method-watch-option.yaml + ref: fullDocumentBeforeChange +post: | + .. versionadded: 1.13 +--- source: file: apiargs-method-watch-option.yaml ref: maxAwaitTimeMS @@ -34,6 +49,12 @@ source: file: apiargs-common-option.yaml ref: session --- +source: + file: apiargs-method-watch-option.yaml + ref: showExpandedEvents +post: | + .. versionadded:: 1.13 +--- source: file: apiargs-method-watch-option.yaml ref: startAfter diff --git a/docs/includes/apiargs-MongoDBCollection-common-option.yaml b/docs/includes/apiargs-MongoDBCollection-common-option.yaml index 643308ea5..fb919ea42 100644 --- a/docs/includes/apiargs-MongoDBCollection-common-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-common-option.yaml @@ -4,9 +4,6 @@ type: array description: | An array of filter documents that determines which array elements to modify for an update operation on an array field. - - This is not supported for server versions prior to 3.6 and will result in an - exception at execution time if used. interface: phpmethod operation: ~ optional: true @@ -17,9 +14,6 @@ type: boolean description: | If ``true``, allows the write operation to circumvent document level validation. Defaults to ``false``. - - This option is available in MongoDB 3.2+ and is ignored for older server - versions, which do not support document level validation. interface: phpmethod operation: ~ optional: true @@ -40,9 +34,6 @@ description: | :manual:`Read concern ` to use for the operation. Defaults to the collection's read concern. - This is not supported for server versions prior to 3.2 and will result in an - exception at execution time if used. - It is not possible to specify a :manual:`read concern ` for individual operations as part of a transaction. Instead, set the ``readConcern`` option when starting the diff --git a/docs/includes/apiargs-MongoDBCollection-common-param.yaml b/docs/includes/apiargs-MongoDBCollection-common-param.yaml index 9c032d151..47100d19a 100644 --- a/docs/includes/apiargs-MongoDBCollection-common-param.yaml +++ b/docs/includes/apiargs-MongoDBCollection-common-param.yaml @@ -25,7 +25,7 @@ description: | Specifies the field and value combinations to update and any relevant update operators. ``$update`` uses MongoDB's :method:`update operators `. Starting with MongoDB 4.2, an `aggregation - pipeline `_ + pipeline `_ can be passed as this parameter. interface: phpmethod operation: ~ diff --git a/docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml index 86edc1d50..b3d19c3e5 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml @@ -11,9 +11,12 @@ source: ref: bypassDocumentValidation --- source: - file: apiargs-aggregate-option.yaml + file: apiargs-common-option.yaml ref: comment post: | + The comment can be any valid BSON type for server versions 4.4 and above. + Earlier server versions only support string values. + .. versionadded:: 1.3 --- source: @@ -28,6 +31,12 @@ source: post: | .. versionadded:: 1.3 --- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.9 +--- source: file: apiargs-common-option.yaml ref: maxTimeMS @@ -72,7 +81,4 @@ source: post: | This only applies when a :ref:`$out ` or :ref:`$merge ` stage is specified. - - This is not supported for server versions prior to 3.4 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBCollection-method-bulkWrite-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-bulkWrite-option.yaml index d5d2e5df4..25d8e24d3 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-bulkWrite-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-bulkWrite-option.yaml @@ -2,6 +2,21 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: bypassDocumentValidation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.13 +--- arg_name: option name: ordered type: boolean diff --git a/docs/includes/apiargs-MongoDBCollection-method-count-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-count-option.yaml index 62227f1cc..3d839e04a 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-count-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-count-option.yaml @@ -2,6 +2,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- arg_name: option name: hint type: string|array|object diff --git a/docs/includes/apiargs-MongoDBCollection-method-countDocuments-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-countDocuments-option.yaml index 2571b0716..da1f01138 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-countDocuments-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-countDocuments-option.yaml @@ -2,6 +2,13 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + The comment can be any valid BSON type for server versions 4.4 and above. + Earlier server versions only support string values. +--- arg_name: option name: hint type: string|array|object diff --git a/docs/includes/apiargs-MongoDBCollection-method-createIndex-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-createIndex-option.yaml index 1a76b33ca..182837674 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-createIndex-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-createIndex-option.yaml @@ -17,6 +17,15 @@ interface: phpmethod operation: ~ optional: true --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- arg_name: option name: unique type: boolean @@ -105,7 +114,4 @@ post: | source: file: apiargs-MongoDBCollection-common-option.yaml ref: writeConcern -post: | - This is not supported for server versions prior to 3.4 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBCollection-method-createIndexes-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-createIndexes-option.yaml index 8808ddd1b..54c33bfa0 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-createIndexes-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-createIndexes-option.yaml @@ -1,3 +1,12 @@ +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-MongoDBCollection-method-createIndex-option.yaml ref: commitQuorum @@ -17,7 +26,4 @@ post: | source: file: apiargs-MongoDBCollection-common-option.yaml ref: writeConcern -post: | - This is not supported for server versions prior to 3.4 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBCollection-method-deleteMany-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-deleteMany-option.yaml index 55f6af9b3..110911f51 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-deleteMany-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-deleteMany-option.yaml @@ -2,6 +2,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: hint @@ -11,6 +20,12 @@ post: | .. versionadded:: 1.7 --- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: session diff --git a/docs/includes/apiargs-MongoDBCollection-method-deleteOne-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-deleteOne-option.yaml index 55f6af9b3..110911f51 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-deleteOne-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-deleteOne-option.yaml @@ -2,6 +2,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: hint @@ -11,6 +20,12 @@ post: | .. versionadded:: 1.7 --- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: session diff --git a/docs/includes/apiargs-MongoDBCollection-method-distinct-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-distinct-option.yaml index 3c2ae6ed2..05a1b2853 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-distinct-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-distinct-option.yaml @@ -2,6 +2,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: maxTimeMS diff --git a/docs/includes/apiargs-MongoDBCollection-method-drop-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-drop-option.yaml index b1f39752a..5eb5efaba 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-drop-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-drop-option.yaml @@ -1,8 +1,17 @@ source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap + file: apiargs-common-option.yaml + ref: comment post: | - This will be used for the returned command result document. + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- +source: + file: apiargs-dropCollection-option.yaml + ref: encryptedFields +post: | + .. versionadded:: 1.13 --- source: file: apiargs-common-option.yaml @@ -12,8 +21,11 @@ post: | --- source: file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern + ref: typeMap post: | - This is not supported for server versions prior to 3.4 and will result in an - exception at execution time if used. + This will be used for the returned command result document. +--- +source: + file: apiargs-MongoDBCollection-common-option.yaml + ref: writeConcern ... diff --git a/docs/includes/apiargs-MongoDBCollection-method-dropIndex-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-dropIndex-option.yaml index 00db41c6c..b87e09b74 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-dropIndex-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-dropIndex-option.yaml @@ -1,3 +1,12 @@ +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: maxTimeMS @@ -19,7 +28,4 @@ post: | source: file: apiargs-MongoDBCollection-common-option.yaml ref: writeConcern -post: | - This is not supported for server versions prior to 3.4 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBCollection-method-dropIndexes-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-dropIndexes-option.yaml index 0b1970c36..aff9f4d75 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-dropIndexes-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-dropIndexes-option.yaml @@ -1,3 +1,12 @@ +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: maxTimeMS @@ -17,7 +26,4 @@ post: | source: file: apiargs-MongoDBCollection-common-option.yaml ref: writeConcern -post: | - This is not supported for server versions prior to 3.4 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBCollection-method-estimateDocumentCount-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-estimateDocumentCount-option.yaml index f70487a16..7289296e9 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-estimateDocumentCount-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-estimateDocumentCount-option.yaml @@ -1,3 +1,12 @@ +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: maxTimeMS diff --git a/docs/includes/apiargs-MongoDBCollection-method-explain-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-explain-option.yaml index 7c6a4dcd0..650633bce 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-explain-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-explain-option.yaml @@ -1,3 +1,14 @@ +source: + file: apiargs-common-option.yaml + ref: comment +post: | + Defaults to the ``comment`` of the explained operation (if any). + + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-MongoDBCollection-common-option.yaml ref: readPreference diff --git a/docs/includes/apiargs-MongoDBCollection-method-find-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-find-option.yaml index d6ff4c532..1b01599ec 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-find-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-find-option.yaml @@ -51,8 +51,7 @@ name: allowDiskUse type: boolean description: | Enables writing to temporary files. When set to ``true``, queries can write - data to the ``_tmp`` sub-directory in the ``dbPath`` directory. The default is - ``false``. + data to the ``_tmp`` sub-directory in the ``dbPath`` directory. interface: phpmethod operation: ~ optional: true @@ -75,15 +74,12 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- -arg_name: option -name: comment -type: string -description: | - A comment to attach to the query to help interpret and trace query - :dbcommand:`profile` data. -interface: phpmethod -operation: ~ -optional: true +source: + file: apiargs-common-option.yaml + ref: comment +post: | + The comment can be any valid BSON type for server versions 4.4 and above. + Earlier server versions only support string values. --- arg_name: option name: cursorType @@ -258,4 +254,10 @@ description: | interface: phpmethod operation: ~ optional: true +--- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.13 ... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOne-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOne-option.yaml index 2371aabe2..139e700ac 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-findOne-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-findOne-option.yaml @@ -75,4 +75,10 @@ source: source: file: apiargs-MongoDBCollection-method-find-option.yaml ref: modifiers +--- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.13 ... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOneAndDelete-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOneAndDelete-option.yaml index 520b4de19..d8f2b3a4a 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-findOneAndDelete-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-findOneAndDelete-option.yaml @@ -10,6 +10,30 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- +source: + file: apiargs-common-option.yaml + ref: hint +post: | + This option is available in MongoDB 4.4+ and will result in an exception at + execution time if specified for an older server version. + + .. versionadded:: 1.7 +--- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: maxTimeMS @@ -29,7 +53,4 @@ post: | source: file: apiargs-MongoDBCollection-common-option.yaml ref: writeConcern -post: | - This is not supported for server versions prior to 3.2 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOneAndReplace-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOneAndReplace-option.yaml index 6b282dd29..b509ac947 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-findOneAndReplace-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-findOneAndReplace-option.yaml @@ -10,6 +10,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: hint @@ -27,6 +36,12 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: bypassDocumentValidation --- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.13 +--- arg_name: option name: returnDocument type: integer @@ -59,7 +74,4 @@ source: source: file: apiargs-MongoDBCollection-common-option.yaml ref: writeConcern -post: | - This is not supported for server versions prior to 3.2 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOneAndUpdate-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOneAndUpdate-option.yaml index 8393d26aa..a8f895f1b 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-findOneAndUpdate-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-findOneAndUpdate-option.yaml @@ -16,6 +16,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: hint @@ -33,6 +42,12 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: bypassDocumentValidation --- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.13 +--- arg_name: option name: returnDocument type: integer @@ -65,7 +80,4 @@ source: source: file: apiargs-MongoDBCollection-common-option.yaml ref: writeConcern -post: | - This is not supported for server versions prior to 3.2 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBCollection-method-insertMany-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-insertMany-option.yaml index 391935780..60f197d2b 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-insertMany-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-insertMany-option.yaml @@ -2,6 +2,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: bypassDocumentValidation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-MongoDBCollection-method-bulkWrite-option.yaml ref: ordered diff --git a/docs/includes/apiargs-MongoDBCollection-method-insertOne-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-insertOne-option.yaml index 61d795c76..adf17cb61 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-insertOne-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-insertOne-option.yaml @@ -2,6 +2,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: bypassDocumentValidation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: session diff --git a/docs/includes/apiargs-MongoDBCollection-method-listIndexes-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-listIndexes-option.yaml index cbfb65016..aa939b950 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-listIndexes-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-listIndexes-option.yaml @@ -1,3 +1,12 @@ +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: maxTimeMS diff --git a/docs/includes/apiargs-MongoDBCollection-method-mapReduce-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-mapReduce-option.yaml index 33ef3892c..478d12dda 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-mapReduce-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-mapReduce-option.yaml @@ -8,6 +8,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- arg_name: option name: finalize type: :php:`MongoDB\\BSON\\Javascript ` @@ -27,7 +36,7 @@ name: jsMode type: boolean description: | Specifies whether to convert intermediate data into BSON format between the - execution of the map and reduce functions. The default is ``false``. + execution of the map and reduce functions. interface: phpmethod operation: ~ optional: true @@ -47,7 +56,7 @@ source: --- arg_name: option name: query -type: document +type: array|object description: | Specifies the selection criteria using query operators for determining the documents input to the map function. @@ -67,7 +76,7 @@ post: | --- arg_name: option name: scope -type: document +type: array|object description: | Specifies global variables that are accessible in the map, reduce, and finalize functions. @@ -94,7 +103,6 @@ name: verbose type: boolean description: | Specifies whether to include the timing information in the result information. - The default is ``true``. interface: phpmethod operation: ~ optional: true diff --git a/docs/includes/apiargs-MongoDBCollection-method-mapReduce-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-mapReduce-param.yaml index e8254f3e2..6a76d6365 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-mapReduce-param.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-mapReduce-param.yaml @@ -30,7 +30,7 @@ optional: false --- arg_name: param name: $out -type: string|document +type: string|array|object description: | Specifies where to output the result of the map-reduce operation. You can either output to a collection or return the result inline. On a primary member diff --git a/docs/includes/apiargs-MongoDBCollection-method-rename-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-rename-option.yaml new file mode 100644 index 000000000..0b0b0809c --- /dev/null +++ b/docs/includes/apiargs-MongoDBCollection-method-rename-option.yaml @@ -0,0 +1,33 @@ +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- +source: + file: apiargs-MongoDBCollection-common-option.yaml + ref: typeMap +post: | + This will be used for the returned command result document. +--- +source: + file: apiargs-common-option.yaml + ref: session +--- +source: + file: apiargs-MongoDBCollection-common-option.yaml + ref: writeConcern +--- +arg_name: option +name: dropTarget +type: boolean +description: | + If ``true``, MongoDB will drop the target before renaming the collection. The + default value is ``false``. +interface: phpmethod +operation: ~ +optional: true +... diff --git a/docs/includes/apiargs-MongoDBCollection-method-rename-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-rename-param.yaml new file mode 100644 index 000000000..459b2789d --- /dev/null +++ b/docs/includes/apiargs-MongoDBCollection-method-rename-param.yaml @@ -0,0 +1,25 @@ +arg_name: param +name: $toCollectionName +type: string +description: | + The new name of the collection. +interface: phpmethod +operation: ~ +optional: false +--- +arg_name: param +name: $toDatabaseName +type: string +description: | + The new database name of the collection. If a new database name is not + specified, the database of the original collection will be used. If the new + name specifies a different database, the command copies the collection + to the new database and drops the source collection. +interface: phpmethod +operation: ~ +optional: true +--- +source: + file: apiargs-common-param.yaml + ref: $options +... diff --git a/docs/includes/apiargs-MongoDBCollection-method-replaceOne-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-replaceOne-option.yaml index cfc3a71d4..676296d26 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-replaceOne-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-replaceOne-option.yaml @@ -10,6 +10,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: hint @@ -19,6 +28,12 @@ post: | .. versionadded:: 1.6 --- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: session diff --git a/docs/includes/apiargs-MongoDBCollection-method-updateMany-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-updateMany-option.yaml index b94cb750a..64eec34e0 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-updateMany-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-updateMany-option.yaml @@ -16,6 +16,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: hint @@ -25,6 +34,12 @@ post: | .. versionadded:: 1.6 --- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: session diff --git a/docs/includes/apiargs-MongoDBCollection-method-updateOne-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-updateOne-option.yaml index b94cb750a..64eec34e0 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-updateOne-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-updateOne-option.yaml @@ -16,6 +16,15 @@ source: file: apiargs-MongoDBCollection-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: hint @@ -25,6 +34,12 @@ post: | .. versionadded:: 1.6 --- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: session diff --git a/docs/includes/apiargs-MongoDBCollection-method-watch-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-watch-option.yaml index a873beec5..0a47ae623 100644 --- a/docs/includes/apiargs-MongoDBCollection-method-watch-option.yaml +++ b/docs/includes/apiargs-MongoDBCollection-method-watch-option.yaml @@ -7,10 +7,25 @@ source: file: apiargs-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + The comment can be any valid BSON type for server versions 4.4 and above. + Earlier server versions only support string values. + + .. versionadded:: 1.13 +--- source: file: apiargs-method-watch-option.yaml ref: fullDocument --- +source: + file: apiargs-method-watch-option.yaml + ref: fullDocumentBeforeChange +post: | + .. versionadded: 1.13 +--- source: file: apiargs-method-watch-option.yaml ref: maxAwaitTimeMS @@ -34,6 +49,12 @@ source: file: apiargs-common-option.yaml ref: session --- +source: + file: apiargs-method-watch-option.yaml + ref: showExpandedEvents +post: | + .. versionadded:: 1.13 +--- source: file: apiargs-method-watch-option.yaml ref: startAfter diff --git a/docs/includes/apiargs-MongoDBDatabase-common-option.yaml b/docs/includes/apiargs-MongoDBDatabase-common-option.yaml index 69b4c13da..b03dac7a7 100644 --- a/docs/includes/apiargs-MongoDBDatabase-common-option.yaml +++ b/docs/includes/apiargs-MongoDBDatabase-common-option.yaml @@ -4,9 +4,6 @@ type: :php:`MongoDB\\Driver\\ReadConcern ` description: | :manual:`Read concern ` to use for the operation. Defaults to the database's read concern. - - This is not supported for server versions prior to 3.2 and will result in an - exception at execution time if used. interface: phpmethod operation: ~ optional: true diff --git a/docs/includes/apiargs-MongoDBDatabase-method-aggregate-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-aggregate-option.yaml index 5d80f32aa..f59192a0f 100644 --- a/docs/includes/apiargs-MongoDBDatabase-method-aggregate-option.yaml +++ b/docs/includes/apiargs-MongoDBDatabase-method-aggregate-option.yaml @@ -11,8 +11,11 @@ source: ref: bypassDocumentValidation --- source: - file: apiargs-aggregate-option.yaml + file: apiargs-common-option.yaml ref: comment +post: | + The comment can be any valid BSON type for server versions 4.4 and above. + Earlier server versions only support string values. --- source: file: apiargs-aggregate-option.yaml @@ -22,6 +25,12 @@ source: file: apiargs-common-option.yaml ref: hint --- +source: + file: apiargs-common-option.yaml + ref: let +post: | + .. versionadded:: 1.9 +--- source: file: apiargs-common-option.yaml ref: maxTimeMS @@ -48,7 +57,4 @@ source: post: | This only applies when a :ref:`$out ` or :ref:`$merge ` stage is specified. - - This is not supported for server versions prior to 3.4 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-createCollection-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-createCollection-option.yaml index 76dbf405a..ceb7dd075 100644 --- a/docs/includes/apiargs-MongoDBDatabase-method-createCollection-option.yaml +++ b/docs/includes/apiargs-MongoDBDatabase-method-createCollection-option.yaml @@ -27,6 +27,39 @@ interface: phpmethod operation: ~ optional: true --- +arg_name: option +name: changeStreamPreAndPostImages +type: document +description: | + Used to configure support for pre- and post-images in change streams. See the + :manual:`create ` command documentation for more + information. + + This option is available in MongoDB 6.0+ and will result in an exception at + execution time if specified for an older server version. + + .. versionadded:: 1.13 +interface: phpmethod +operation: ~ +optional: true +--- +arg_name: option +name: clusteredIndex +type: document +description: | + A clustered index specification. See + :manual:`Clustered Collections ` or the + :manual:`create ` command documentation for more + information. + + This option is available in MongoDB 5.3+ and will result in an exception at + execution time if specified for an older server version. + + .. versionadded:: 1.13 +interface: phpmethod +operation: ~ +optional: true +--- source: file: apiargs-common-option.yaml ref: collation @@ -34,6 +67,49 @@ pre: | Specifies the :manual:`collation ` for the collection. --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- +arg_name: option +name: encryptedFields +type: document +description: | + A document describing encrypted fields for queryable encryption. If omitted, + the ``encryptedFieldsMap`` option within the ``autoEncryption`` driver option + will be consulted. See + `Field Encryption and Queryability `_ + in the MongoDB manual for more information. + + This option is available in MongoDB 7.0+ and will result in an exception at + execution time if specified for an older server version. + + .. versionadded:: 1.13 +interface: phpmethod +operation: ~ +optional: true +--- +arg_name: option +name: expireAfterSeconds +type: integer +description: | + Used to automatically delete documents in time series collections. See the + :manual:`create ` command documentation for more + information. + + This option is available in MongoDB 5.0+ and will result in an exception at + execution time if specified for an older server version. + + .. versionadded:: 1.9 +interface: phpmethod +operation: ~ +optional: true +--- arg_name: option name: flags type: integer @@ -67,8 +143,10 @@ description: | collection. The ``indexOptionDefaults`` option accepts a ``storageEngine`` document, - which should take the following form:: + which should take the following form: + .. code-block:: none + { : } Storage engine configurations specified when creating indexes are validated @@ -96,6 +174,20 @@ source: file: apiargs-common-option.yaml ref: maxTimeMS --- +arg_name: option +name: pipeline +type: array +description: | + An array that consists of the aggregation pipeline stage(s), which will be + applied to the collection or view specified by ``viewOn``. See the + :manual:`create ` command documentation for more + information. + + .. versionadded:: 1.13 +interface: phpmethod +operation: ~ +optional: true +--- source: file: apiargs-common-option.yaml ref: session @@ -122,8 +214,10 @@ description: | Allows users to specify configuration to the storage engine on a per-collection basis when creating a collection. The value of the - ``storageEngine`` option should take the following form:: + ``storageEngine`` option should take the following form: + .. code-block:: none + { : } Storage engine configurations specified when creating collections are @@ -133,6 +227,22 @@ interface: phpmethod operation: ~ optional: true --- +arg_name: option +name: timeseries +type: array|object +description: | + An object containing options for creating time series collections. See the + :manual:`create ` command documentation for + supported options. + + This option is available in MongoDB 5.0+ and will result in an exception at + execution time if specified for an older server version. + + .. versionadded:: 1.9 +interface: phpmethod +operation: ~ +optional: true +--- source: file: apiargs-MongoDBDatabase-common-option.yaml ref: typeMap @@ -230,10 +340,24 @@ interface: phpmethod operation: ~ optional: true --- +arg_name: option +name: viewOn +type: string +description: | + The name of the source collection or view from which to create the view. + + .. note:: + + The name is not the full namespace of the collection or view (i.e. it does + not include the database name). Views must be created in the same databases + as the source collection or view. + + .. versionadded:: 1.13 +interface: phpmethod +operation: ~ +optional: true +--- source: file: apiargs-MongoDBDatabase-common-option.yaml ref: writeConcern -post: | - This is not supported for server versions prior to 3.4 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-createEncryptedCollection-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-createEncryptedCollection-param.yaml new file mode 100644 index 000000000..6d56d6785 --- /dev/null +++ b/docs/includes/apiargs-MongoDBDatabase-method-createEncryptedCollection-param.yaml @@ -0,0 +1,47 @@ +source: + file: apiargs-common-param.yaml + ref: $collectionName +replacement: + subject: "encrypted collection" + action: " to create" +--- +arg_name: param +name: $clientEncryption +type: :php:`MongoDB\\Driver\\ClientEncryption ` +description: | + The ClientEncryption object used to create data keys. +interface: phpmethod +operation: ~ +optional: false +--- +arg_name: param +name: $kmsProvider +type: string +description: | + KMS provider (e.g. "local", "aws") that will be used to encrypt new data keys. + This corresponds to the ``$kmsProvider`` parameter for + :php:`MongoDB\\Driver\\ClientEncryption::createDataKey() `. +interface: phpmethod +operation: ~ +optional: false +--- +arg_name: param +name: $masterKey +type: array|null +description: | + KMS-specific key options that will be used to encrypt new data keys. This + corresponds to the ``masterKey`` option for + :php:`MongoDB\\Driver\\ClientEncryption::createDataKey() `. + + If ``$kmsProvider`` is "local", this should be ``null``. +interface: phpmethod +operation: ~ +optional: false +--- +source: + file: apiargs-common-param.yaml + ref: $options +optional: false +post: | + The ``encryptedFields`` option is required. +... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-drop-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-drop-option.yaml index 899696857..e725fc1be 100644 --- a/docs/includes/apiargs-MongoDBDatabase-method-drop-option.yaml +++ b/docs/includes/apiargs-MongoDBDatabase-method-drop-option.yaml @@ -1,3 +1,12 @@ +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: session @@ -13,7 +22,4 @@ post: | source: file: apiargs-MongoDBDatabase-common-option.yaml ref: writeConcern -post: | - This is not supported for server versions prior to 3.4 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-dropCollection-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-dropCollection-option.yaml index 899696857..0034b5e88 100644 --- a/docs/includes/apiargs-MongoDBDatabase-method-dropCollection-option.yaml +++ b/docs/includes/apiargs-MongoDBDatabase-method-dropCollection-option.yaml @@ -1,3 +1,18 @@ +source: + file: apiargs-dropCollection-option.yaml + ref: encryptedFields +post: | + .. versionadded:: 1.13 +--- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: session @@ -13,7 +28,4 @@ post: | source: file: apiargs-MongoDBDatabase-common-option.yaml ref: writeConcern -post: | - This is not supported for server versions prior to 3.4 and will result in an - exception at execution time if used. ... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-listCollections-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-listCollections-option.yaml index 0d9ab968d..bacf7280a 100644 --- a/docs/includes/apiargs-MongoDBDatabase-method-listCollections-option.yaml +++ b/docs/includes/apiargs-MongoDBDatabase-method-listCollections-option.yaml @@ -1,4 +1,28 @@ arg_name: option +name: authorizedCollections +type: boolean +description: | + A flag that determines which collections are returned based on the user + privileges when access control is enabled. For more information, see the + `listCollections command documentation `_. + + For servers < 4.0, this option is ignored. + + .. versionadded:: 1.12 +interface: phpmethod +operation: ~ +optional: true +--- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- +arg_name: option name: filter type: array|object description: | diff --git a/docs/includes/apiargs-MongoDBDatabase-method-modifyCollection-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-modifyCollection-option.yaml index ca8d6fef1..b27c16cfb 100644 --- a/docs/includes/apiargs-MongoDBDatabase-method-modifyCollection-option.yaml +++ b/docs/includes/apiargs-MongoDBDatabase-method-modifyCollection-option.yaml @@ -1,3 +1,12 @@ +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- source: file: apiargs-common-option.yaml ref: session diff --git a/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-option.yaml new file mode 100644 index 000000000..bb026bd9c --- /dev/null +++ b/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-option.yaml @@ -0,0 +1,33 @@ +source: + file: apiargs-common-option.yaml + ref: comment +post: | + This is not supported for server versions prior to 4.4 and will result in an + exception at execution time if used. + + .. versionadded:: 1.13 +--- +source: + file: apiargs-common-option.yaml + ref: session +--- +source: + file: apiargs-MongoDBDatabase-common-option.yaml + ref: typeMap +post: | + This will be used for the returned command result document. +--- +source: + file: apiargs-MongoDBDatabase-common-option.yaml + ref: writeConcern +--- +arg_name: option +name: dropTarget +type: boolean +description: | + If ``true``, MongoDB will drop the target before renaming the collection. The + default value is ``false``. +interface: phpmethod +operation: ~ +optional: true +... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-param.yaml new file mode 100644 index 000000000..3043f4dd5 --- /dev/null +++ b/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-param.yaml @@ -0,0 +1,34 @@ +arg_name: param +name: $fromCollectionName +type: string +description: | + The name of the collection to rename. +interface: phpmethod +operation: ~ +optional: false +--- +arg_name: param +name: $toCollectionName +type: string +description: | + The new name of the collection. +interface: phpmethod +operation: ~ +optional: false +--- +arg_name: param +name: $toDatabaseName +type: string +description: | + The new database name of the collection. If a new database name is not + specified, the current database will be used. If the new name specifies a + different database, the command copies the collection to the new database + and drops the source collection. +interface: phpmethod +operation: ~ +optional: true +--- +source: + file: apiargs-common-param.yaml + ref: $options +... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-watch-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-watch-option.yaml index b1ad02793..b24efbec2 100644 --- a/docs/includes/apiargs-MongoDBDatabase-method-watch-option.yaml +++ b/docs/includes/apiargs-MongoDBDatabase-method-watch-option.yaml @@ -7,10 +7,25 @@ source: file: apiargs-common-option.yaml ref: collation --- +source: + file: apiargs-common-option.yaml + ref: comment +post: | + The comment can be any valid BSON type for server versions 4.4 and above. + Earlier server versions only support string values. + + .. versionadded:: 1.13 +--- source: file: apiargs-method-watch-option.yaml ref: fullDocument --- +source: + file: apiargs-method-watch-option.yaml + ref: fullDocumentBeforeChange +post: | + .. versionadded: 1.13 +--- source: file: apiargs-method-watch-option.yaml ref: maxAwaitTimeMS @@ -34,6 +49,12 @@ source: file: apiargs-common-option.yaml ref: session --- +source: + file: apiargs-method-watch-option.yaml + ref: showExpandedEvents +post: | + .. versionadded:: 1.13 +--- source: file: apiargs-method-watch-option.yaml ref: startAfter diff --git a/docs/includes/apiargs-aggregate-option.yaml b/docs/includes/apiargs-aggregate-option.yaml index 3de7a09db..e5b410c4f 100644 --- a/docs/includes/apiargs-aggregate-option.yaml +++ b/docs/includes/apiargs-aggregate-option.yaml @@ -3,8 +3,7 @@ name: allowDiskUse type: boolean description: | Enables writing to temporary files. When set to ``true``, aggregation stages - can write data to the ``_tmp`` sub-directory in the ``dbPath`` directory. The - default is ``false``. + can write data to the ``_tmp`` sub-directory in the ``dbPath`` directory. interface: phpmethod operation: ~ optional: true @@ -13,9 +12,14 @@ arg_name: option name: batchSize type: integer description: | - Specifies the initial batch size for the cursor. A batchSize of ``0`` means an - empty first batch and is useful for quickly returning a cursor or failure - message without doing significant server-side work. + Specifies the batch size for the cursor, which will apply to both the initial + ``aggregate`` command and any subsequent ``getMore`` commands. This determines + the maximum number of documents to return in each response from the server. + + A batchSize of ``0`` is special in that and will only apply to the initial + ``aggregate`` command; subsequent ``getMore`` commands will use the server's + default batch size. This may be useful for quickly returning a cursor or + failure from ``aggregate`` without doing significant server-side work. interface: phpmethod operation: ~ optional: true @@ -26,19 +30,6 @@ source: post: | This only applies when using the :ref:`$out ` and :ref:`$out ` stages. - - Document validation requires MongoDB 3.2 or later: if you are using an earlier - version of MongoDB, this option will be ignored. ---- -arg_name: option -name: comment -type: string -description: | - Users can specify an arbitrary string to help trace the operation through the - database profiler, currentOp, and logs. -interface: phpmethod -operation: ~ -optional: true --- arg_name: option name: explain diff --git a/docs/includes/apiargs-common-option.yaml b/docs/includes/apiargs-common-option.yaml index 41f918205..cae141871 100644 --- a/docs/includes/apiargs-common-option.yaml +++ b/docs/includes/apiargs-common-option.yaml @@ -8,9 +8,18 @@ description: | mandatory; all other collation fields are optional. For descriptions of the fields, see :manual:`Collation Document `. - - This option is available in MongoDB 3.4+ and will result in an exception at - execution time if specified for an older server version. +interface: phpmethod +operation: ~ +optional: true +--- +arg_name: option +name: comment +type: mixed +description: | + Enables users to specify an arbitrary comment to help trace the operation + through the :manual:`database profiler `, + :manual:`currentOp ` output, and + :manual:`logs `. interface: phpmethod operation: ~ optional: true @@ -27,6 +36,20 @@ operation: ~ optional: true --- arg_name: option +name: let +type: array|object +description: | + Map of parameter names and values. Values must be constant or closed + expressions that do not reference document fields. Parameters can then be + accessed as variables in an aggregate expression context (e.g. ``$$var``). + + This is not supported for server versions prior to 5.0 and will result in an + exception at execution time if used. +interface: phpmethod +operation: ~ +optional: true +--- +arg_name: option name: maxTimeMS type: integer description: | @@ -68,8 +91,6 @@ name: session type: :php:`MongoDB\\Driver\\Session ` description: | Client session to associate with the operation. - - Sessions are not supported for server versions prior to 3.6. interface: phpmethod operation: ~ optional: true diff --git a/docs/includes/apiargs-dropCollection-option.yaml b/docs/includes/apiargs-dropCollection-option.yaml new file mode 100644 index 000000000..158c4a438 --- /dev/null +++ b/docs/includes/apiargs-dropCollection-option.yaml @@ -0,0 +1,21 @@ +arg_name: option +name: encryptedFields +type: array|object +description: | + A document describing encrypted fields for queryable encryption. If omitted, + the ``encryptedFieldsMap`` option within the ``autoEncryption`` driver option + will be consulted. If ``encryptedFieldsMap`` was defined but does not specify + this collection, the library will make a final attempt to consult the + server-side value for ``encryptedFields``. See + `Field Encryption and Queryability `_ + in the MongoDB manual for more information. + + .. note:: + + This option is not passed to the :manual:`drop ` + command. The library uses it to determine related metadata collections that + should be dropped in addition to an encrypted collection. +interface: phpmethod +operation: ~ +optional: true +... diff --git a/docs/includes/apiargs-method-watch-option.yaml b/docs/includes/apiargs-method-watch-option.yaml index 89887884f..d09278286 100644 --- a/docs/includes/apiargs-method-watch-option.yaml +++ b/docs/includes/apiargs-method-watch-option.yaml @@ -3,8 +3,17 @@ arg_name: option name: batchSize type: integer description: | - Specifies the maximum number of change events to return in each batch of the - response from the MongoDB cluster. + Specifies the batch size for the cursor, which will apply to both the initial + ``aggregate`` command and any subsequent ``getMore`` commands. This determines + the maximum number of change events to return in each response from the + server. + + .. note:: + + Irrespective of the ``batchSize`` option, the initial ``aggregate`` command + response for a change stream generally does not include any documents + unless another option is used to configure its starting point (e.g. + ``startAfter``). interface: phpmethod operation: ~ optional: true @@ -13,14 +22,53 @@ arg_name: option name: fullDocument type: string description: | - Allowed values are 'default' and 'updateLookup'. Defaults to 'default'. - When set to 'updateLookup', the change notification for partial updates will - include both a delta describing the changes to the document, as well as a - copy of the entire document that was changed from some time after the change - occurred. The following values are supported: + Determines how the "fullDocument" response field will be populated for update + operations. + + By default, change streams only return the delta of fields (via an + "updateDescription" field) for update operations and "fullDocument" is + omitted. Insert and replace operations always include the "fullDocument" + field. Delete operations omit the field as the document no longer exists. + + Specify "updateLookup" to return the current majority-committed version of the + updated document. + + MongoDB 6.0+ allows returning the post-image of the modified document if the + collection has ``changeStreamPreAndPostImages`` enabled. Specify + "whenAvailable" to return the post-image if available or a null value if not. + Specify "required" to return the post-image if available or raise an error if + not. + + The following values are supported: - - ``MongoDB\Operation\Watch::FULL_DOCUMENT_DEFAULT`` (*default*) - ``MongoDB\Operation\Watch::FULL_DOCUMENT_UPDATE_LOOKUP`` + - ``MongoDB\Operation\Watch::FULL_DOCUMENT_WHEN_AVAILABLE`` + - ``MongoDB\Operation\Watch::FULL_DOCUMENT_REQUIRED`` + + .. note:: + + This is an option of the ``$changeStream`` pipeline stage. +interface: phpmethod +operation: ~ +optional: true +--- +arg_name: option +name: fullDocumentBeforeChange +type: string +description: | + Determines how the "fullDocumentBeforeChange" response field will be + populated. By default, the field is omitted. + + MongoDB 6.0+ allows returning the pre-image of the modified document if the + collection has ``changeStreamPreAndPostImages`` enabled. Specify + "whenAvailable" to return the pre-image if available or a null value if not. + Specify "required" to return the pre-image if available or raise an error if + not. + + The following values are supported: + + - ``MongoDB\Operation\Watch::FULL_DOCUMENT_BEFORE_CHANGE_WHEN_AVAILABLE`` + - ``MongoDB\Operation\Watch::FULL_DOCUMENT_BEFORE_CHANGE_REQUIRED`` .. note:: @@ -58,6 +106,31 @@ operation: ~ optional: true --- arg_name: option +name: showExpandedEvents +type: boolean +description: | + If true, instructs the server to include additional DDL events in the change + stream. The additional events that may be included are: + + - ``createIndexes`` + - ``dropIndexes`` + - ``modify`` + - ``create`` + - ``shardCollection`` + - ``reshardCollection`` (server 6.1+) + - ``refineCollectionShardKey`` (server 6.1+) + + This is not supported for server versions prior to 6.0 and will result in an + exception at execution time if used. + + .. note:: + + This is an option of the ``$changeStream`` pipeline stage. +interface: phpmethod +operation: ~ +optional: true +--- +arg_name: option name: startAfter type: array|object description: | diff --git a/docs/index.txt b/docs/index.txt index ffdd4b16f..5cbf328a4 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -27,18 +27,23 @@ following pages should help you get started: - :doc:`/tutorial/install-php-library` +- :doc:`/tutorial/connecting` + - :doc:`/tutorial/crud` - :doc:`/tutorial/commands` - :doc:`/tutorial/gridfs` +- :doc:`/tutorial/modeling-bson-data` + - :doc:`/reference/bson` -If you have previously worked with the -`legacy PHP driver `_ (i.e. ``mongo`` -extension), it will be helpful to review the :doc:`/upgrade` for a summary of -API changes between the old driver and this library. +Code examples can be found in the ``examples`` directory in the source code. + +If you have previously worked with the legacy ``mongo`` extension, it will be +helpful to review the :doc:`/upgrade` for a summary of API changes between the +old driver and this library. New to MongoDB? --------------- @@ -65,3 +70,4 @@ encounter in the library documentation: /tutorial /upgrade /reference + FAQ diff --git a/docs/reference/bson.txt b/docs/reference/bson.txt index c366ea8a8..b9fbbaa2c 100644 --- a/docs/reference/bson.txt +++ b/docs/reference/bson.txt @@ -14,15 +14,15 @@ Overview -------- MongoDB stores data records as BSON documents. BSON is a binary representation -of :term:`JSON` documents, though it contains more data types than JSON. For the -BSON spec, see `bsonspec.org `_. +of JSON documents, though it contains more data types than JSON. For the BSON +spec, see `bsonspec.org `_. By default, the |php-library| returns BSON documents as :phpclass:`MongoDB\\Model\\BSONDocument` objects and BSON arrays as :phpclass:`MongoDB\\Model\\BSONArray` objects, respectively. -BSON Classes ------------- +Classes +------- .. phpclass:: MongoDB\\Model\\BSONArray @@ -49,213 +49,3 @@ BSON Classes class. During BSON and JSON serialization, instances of this class will serialize as a document type (:php:`object casting ` is used internally). - -.. _php-type-map: - -Type Maps ---------- - -Most methods that read data from MongoDB support a ``typeMap`` option, which -allows control over how BSON is converted to PHP. Additionally, -the :phpclass:`MongoDB\\Client`, :phpclass:`MongoDB\\Database`, and -:phpclass:`MongoDB\\Collection` classes accept a ``typeMap`` option, which can -be used to specify a default type map to apply to any supporting methods and -selected classes (e.g. :phpmethod:`MongoDB\\Client::selectDatabase()`). - -The :phpclass:`MongoDB\\Client`, :phpclass:`MongoDB\\Database`, and -:phpclass:`MongoDB\\Collection` classes use the following type map by -default: - -.. code-block:: php - - [ - 'array' => 'MongoDB\Model\BSONArray', - 'document' => 'MongoDB\Model\BSONDocument', - 'root' => 'MongoDB\Model\BSONDocument', - ] - -The type map above will convert BSON documents and arrays to -:phpclass:`MongoDB\\Model\\BSONDocument` and -:phpclass:`MongoDB\\Model\\BSONArray` objects, respectively. The ``root`` and -``document`` keys are used to distinguish the top-level BSON document from -embedded documents, respectively. - -A type map may specify any class that implements -:php:`MongoDB\\BSON\\Unserializable ` as well as -``"array"``, ``"stdClass``", and ``"object"`` (``"stdClass``" and ``"object"`` -are aliases of one another). - -.. seealso:: :php:`Deserialization from BSON ` in the PHP manual - -``Persistable`` Classes ------------------------ - -The driver's :php:`persistence specification ` outlines how -classes implementing its :php:`MongoDB\\BSON\\Persistable -` interface are serialized to and deserialized from -BSON. The :php:`Persistable ` interface is analogous -to PHP's :php:`Serializable interface `. - -The driver automatically handles serialization and deserialization for classes -implementing the :php:`Persistable ` interface without -requiring the use of the ``typeMap`` option. This is done by encoding the name -of the PHP class in a special property within the BSON document. - -.. note:: - - When deserializing a PHP variable from BSON, the encoded class name of a - :php:`Persistable ` object will override any class - specified in the type map, but it will not override ``"array"`` and - ``"stdClass"`` or ``"object"``. This is discussed in the - :php:`persistence specification ` but it bears - repeating. - -Consider the following class definition: - -.. code-block:: php - - id = new MongoDB\BSON\ObjectId; - $this->name = (string) $name; - $this->createdAt = new MongoDB\BSON\UTCDateTime; - } - - function bsonSerialize() - { - return [ - '_id' => $this->id, - 'name' => $this->name, - 'createdAt' => $this->createdAt, - ]; - } - - function bsonUnserialize(array $data) - { - $this->id = $data['_id']; - $this->name = $data['name']; - $this->createdAt = $data['createdAt']; - } - } - -The following example constructs a ``Person`` object, inserts it into the -database, and reads it back as an object of the same type: - -.. code-block:: php - - test->persons; - - $result = $collection->insertOne(new Person('Bob')); - - $person = $collection->findOne(['_id' => $result->getInsertedId()]); - - var_dump($person); - -The output would then resemble: - -.. code-block:: none - - object(Person)#18 (3) { - ["id":"Person":private]=> - object(MongoDB\BSON\ObjectId)#15 (1) { - ["oid"]=> - string(24) "56fad2c36118fd2e9820cfc1" - } - ["name":"Person":private]=> - string(3) "Bob" - ["createdAt":"Person":private]=> - object(MongoDB\BSON\UTCDateTime)#17 (1) { - ["milliseconds"]=> - int(1459278531218) - } - } - -The same document in the MongoDB shell might display as: - -.. code-block:: js - - { - "_id" : ObjectId("56fad2c36118fd2e9820cfc1"), - "__pclass" : BinData(128,"UGVyc29u"), - "name" : "Bob", - "createdAt" : ISODate("2016-03-29T19:08:51.218Z") - } - -.. note:: - - :php:`MongoDB\\BSON\\Persistable ` may only be used - for root and embedded BSON documents. It may not be used for BSON arrays. - -Emulating the Legacy Driver ---------------------------- - -The legacy :php:`mongo extension ` returned both BSON documents and -arrays as PHP arrays. While PHP arrays are convenient to work with, this -behavior was problematic: - -- Different BSON types could deserialize to the same PHP value (e.g. - ``{"0": "foo"}`` and ``["foo"]``), which made it impossible to infer the - original BSON type. - -- Numerically-indexed PHP arrays would be serialized as BSON documents if there - was a gap in their key sequence. Such gaps were easily caused by unsetting a - key to remove an element and forgetting to numerically reindex the array. - -The |php-library|'s :phpclass:`BSONDocument ` and -:phpclass:`BSONArray ` classes address these concerns -by preserving the BSON type information during serialization and -deserialization; however, some users may still prefer the legacy behavior. If -desired, you can use the ``typeMap`` option to have the library return -everything as a PHP array: - -.. code-block:: php - - [ - 'array' => 'array', - 'document' => 'array', - 'root' => 'array', - ], - ] - ); - - $document = $client->test->zips->findOne(['_id' => '94301']); - - var_dump($document); - -The above example would output something similar to: - -.. code-block:: php - - array(5) { - ["_id"]=> - string(5) "94301" - ["city"]=> - string(9) "PALO ALTO" - ["loc"]=> - array(2) { - [0]=> - float(-122.149685) - [1]=> - float(37.444324) - } - ["pop"]=> - int(15965) - ["state"]=> - string(2) "CA" - } diff --git a/docs/reference/class/MongoDBCollection.txt b/docs/reference/class/MongoDBCollection.txt index 4ccd2c177..9c9aa3134 100644 --- a/docs/reference/class/MongoDBCollection.txt +++ b/docs/reference/class/MongoDBCollection.txt @@ -23,7 +23,7 @@ Definition select a collection from the library's :phpclass:`MongoDB\\Client` or :phpclass:`MongoDB\\Database` classes. A collection may also be cloned from an existing :phpclass:`MongoDB\\Collection` object via the - :phpmethod:`withOptions() ` method. + :phpmethod:`withOptions() ` method. :phpclass:`MongoDB\\Collection` supports the :php:`readConcern `, :php:`readPreference @@ -41,17 +41,16 @@ Definition Type Map Limitations -------------------- - The :manual:`aggregate ` (when not using a - cursor), :manual:`distinct `, and - :manual:`findAndModify ` helpers do not - support a ``typeMap`` option due to a driver limitation. The - :phpmethod:`aggregate() `, - :phpmethod:`distinct() `, - :phpmethod:`findOneAndReplace() `, - :phpmethod:`findOneAndUpdate() `, and - :phpmethod:`findOneAndDelete() ` - methods return BSON documents as `stdClass` objects and BSON arrays as - arrays. +The :manual:`aggregate ` (when not using a +cursor), :manual:`distinct `, and +:manual:`findAndModify ` helpers do not +support a ``typeMap`` option due to a driver limitation. The +:phpmethod:`aggregate() `, +:phpmethod:`distinct() `, +:phpmethod:`findOneAndReplace() `, +:phpmethod:`findOneAndUpdate() `, and +:phpmethod:`findOneAndDelete() ` +methods return BSON documents as ``stdClass`` objects and BSON arrays as arrays. Methods ------- @@ -91,6 +90,7 @@ Methods /reference/method/MongoDBCollection-insertOne /reference/method/MongoDBCollection-listIndexes /reference/method/MongoDBCollection-mapReduce + /reference/method/MongoDBCollection-rename /reference/method/MongoDBCollection-replaceOne /reference/method/MongoDBCollection-updateMany /reference/method/MongoDBCollection-updateOne diff --git a/docs/reference/class/MongoDBDatabase.txt b/docs/reference/class/MongoDBDatabase.txt index 464c32c50..ac0e0d16c 100644 --- a/docs/reference/class/MongoDBDatabase.txt +++ b/docs/reference/class/MongoDBDatabase.txt @@ -22,7 +22,7 @@ Definition :php:`MongoDB\\Driver\\Manager ` class or select a database from the library's :phpclass:`MongoDB\\Client` class. A database may also be cloned from an existing :phpclass:`MongoDB\\Database` - object via the :phpmethod:`withOptions() ` + object via the :phpmethod:`withOptions() ` method. :phpclass:`MongoDB\\Database` supports the :php:`readConcern @@ -48,6 +48,7 @@ Methods /reference/method/MongoDBDatabase-aggregate /reference/method/MongoDBDatabase-command /reference/method/MongoDBDatabase-createCollection + /reference/method/MongoDBDatabase-createEncryptedCollection /reference/method/MongoDBDatabase-drop /reference/method/MongoDBDatabase-dropCollection /reference/method/MongoDBDatabase-getDatabaseName @@ -59,6 +60,7 @@ Methods /reference/method/MongoDBDatabase-listCollectionNames /reference/method/MongoDBDatabase-listCollections /reference/method/MongoDBDatabase-modifyCollection + /reference/method/MongoDBDatabase-renameCollection /reference/method/MongoDBDatabase-selectCollection /reference/method/MongoDBDatabase-selectGridFSBucket /reference/method/MongoDBDatabase-watch diff --git a/docs/reference/class/MongoDBGridFSBucket.txt b/docs/reference/class/MongoDBGridFSBucket.txt index cea877f43..0fdeb5cb8 100644 --- a/docs/reference/class/MongoDBGridFSBucket.txt +++ b/docs/reference/class/MongoDBGridFSBucket.txt @@ -25,7 +25,7 @@ Definition You can construct a GridFS bucket using the driver's :php:`Manager ` class, or select a bucket from the library's :phpclass:`MongoDB\\Database` class via the - :phpmethod:`selectGridFSBucket() ` + :phpmethod:`selectGridFSBucket() ` method. Methods diff --git a/docs/reference/enumeration-classes.txt b/docs/reference/enumeration-classes.txt index 7eac097e3..bb2a629a8 100644 --- a/docs/reference/enumeration-classes.txt +++ b/docs/reference/enumeration-classes.txt @@ -43,8 +43,11 @@ Methods /reference/method/MongoDBModelCollectionInfo-getCappedMax /reference/method/MongoDBModelCollectionInfo-getCappedSize + /reference/method/MongoDBModelCollectionInfo-getIdIndex + /reference/method/MongoDBModelCollectionInfo-getInfo /reference/method/MongoDBModelCollectionInfo-getName /reference/method/MongoDBModelCollectionInfo-getOptions + /reference/method/MongoDBModelCollectionInfo-getType /reference/method/MongoDBModelCollectionInfo-isCapped ---- diff --git a/docs/reference/exception-classes.txt b/docs/reference/exception-classes.txt index 289e4ffa4..20ae7067e 100644 --- a/docs/reference/exception-classes.txt +++ b/docs/reference/exception-classes.txt @@ -30,6 +30,21 @@ MongoDB\\Exception\\BadMethodCallException ---- +MongoDB\\Exception\\CreateEncryptedCollectionException +------------------------------------------------------ + +.. phpclass:: MongoDB\\Exception\\CreateEncryptedCollectionException + + Thrown by :phpmethod:`MongoDB\\Database::createEncryptedCollection()` if any + error is encountered while creating data keys or creating the collection. The + original exception and modified ``encryptedFields`` option can be accessed + via the ``getPrevious()`` and ``getEncryptedFields()`` methods, respectively. + + This class extends the library's :phpclass:`RuntimeException + ` class. + +---- + MongoDB\\Exception\\InvalidArgumentException -------------------------------------------- @@ -70,12 +85,6 @@ MongoDB\\Exception\\UnsupportedException selected server. It is used sparingly in cases where silently ignoring the unsupported option might otherwise lead to unexpected behavior. - For example, the ``collation`` option for - :phpmethod:`MongoDB\\Collection::deleteOne()` is only supported by - MongoDB 3.4+. Since collation determines how a document is matched, silently - ignoring the option for an older server version could result in an - unintended document being deleted. - This class extends the library's :phpclass:`RuntimeException ` class. diff --git a/docs/reference/method/MongoDBChangeStream-current.txt b/docs/reference/method/MongoDBChangeStream-current.txt index 8057dbaa2..a85786073 100644 --- a/docs/reference/method/MongoDBChangeStream-current.txt +++ b/docs/reference/method/MongoDBChangeStream-current.txt @@ -103,4 +103,4 @@ See Also - :ref:`Tailable Cursor Iteration ` - :manual:`Change Streams ` documentation in the MongoDB manual - :manual:`Change Events ` documentation in the - MongoDB manual \ No newline at end of file + MongoDB manual diff --git a/docs/reference/method/MongoDBChangeStream-getCursorId.txt b/docs/reference/method/MongoDBChangeStream-getCursorId.txt index 1938d3c00..e7004f441 100644 --- a/docs/reference/method/MongoDBChangeStream-getCursorId.txt +++ b/docs/reference/method/MongoDBChangeStream-getCursorId.txt @@ -43,7 +43,9 @@ This example reports the cursor ID for a change stream. var_dump($changeStream->getCursorId()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\CursorId)#5 (1) { ["id"]=> diff --git a/docs/reference/method/MongoDBChangeStream-key.txt b/docs/reference/method/MongoDBChangeStream-key.txt index 6225c11f6..de4754b4f 100644 --- a/docs/reference/method/MongoDBChangeStream-key.txt +++ b/docs/reference/method/MongoDBChangeStream-key.txt @@ -73,4 +73,4 @@ See Also - :phpmethod:`MongoDB\\Database::watch()` - :php:`Iterator::key() ` - :ref:`Tailable Cursor Iteration ` -- :manual:`Change Streams ` documentation in the MongoDB manual \ No newline at end of file +- :manual:`Change Streams ` documentation in the MongoDB manual diff --git a/docs/reference/method/MongoDBClient-dropDatabase.txt b/docs/reference/method/MongoDBClient-dropDatabase.txt index ccb303ffb..3e963ee5c 100644 --- a/docs/reference/method/MongoDBClient-dropDatabase.txt +++ b/docs/reference/method/MongoDBClient-dropDatabase.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function dropDatabase($databaseName, array $options []): array|object + function dropDatabase(string $databaseName, array $options = []): array|object This method has the following parameters: @@ -58,7 +58,9 @@ The following example drops the ``test`` database: var_dump($result); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#8 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBClient-getReadConcern.txt b/docs/reference/method/MongoDBClient-getReadConcern.txt index b7c4c60bc..64f2b3d4a 100644 --- a/docs/reference/method/MongoDBClient-getReadConcern.txt +++ b/docs/reference/method/MongoDBClient-getReadConcern.txt @@ -41,7 +41,9 @@ Example var_dump($client->getReadConcern()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\ReadConcern)#5 (1) { ["level"]=> diff --git a/docs/reference/method/MongoDBClient-getReadPreference.txt b/docs/reference/method/MongoDBClient-getReadPreference.txt index 96d8eec59..cd00f206c 100644 --- a/docs/reference/method/MongoDBClient-getReadPreference.txt +++ b/docs/reference/method/MongoDBClient-getReadPreference.txt @@ -42,7 +42,9 @@ Example var_dump($client->getReadPreference()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\ReadPreference)#5 (1) { ["mode"]=> diff --git a/docs/reference/method/MongoDBClient-getTypeMap.txt b/docs/reference/method/MongoDBClient-getTypeMap.txt index 04697f335..138444f06 100644 --- a/docs/reference/method/MongoDBClient-getTypeMap.txt +++ b/docs/reference/method/MongoDBClient-getTypeMap.txt @@ -45,7 +45,9 @@ Example var_dump($client->getTypeMap()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(3) { ["root"]=> diff --git a/docs/reference/method/MongoDBClient-getWriteConcern.txt b/docs/reference/method/MongoDBClient-getWriteConcern.txt index b779aae96..7a3e561c6 100644 --- a/docs/reference/method/MongoDBClient-getWriteConcern.txt +++ b/docs/reference/method/MongoDBClient-getWriteConcern.txt @@ -42,7 +42,9 @@ Example var_dump($client->getWriteConcern()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\WriteConcern)#4 (1) { ["j"]=> diff --git a/docs/reference/method/MongoDBClient-listDatabaseNames.txt b/docs/reference/method/MongoDBClient-listDatabaseNames.txt index 5047b88d7..bf4fb48fe 100644 --- a/docs/reference/method/MongoDBClient-listDatabaseNames.txt +++ b/docs/reference/method/MongoDBClient-listDatabaseNames.txt @@ -59,7 +59,9 @@ The following example lists all databases on the server: var_dump($databaseName); } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(5) "local" string(4) "test" diff --git a/docs/reference/method/MongoDBClient-listDatabases.txt b/docs/reference/method/MongoDBClient-listDatabases.txt index 7f27cee8e..822f98d7c 100644 --- a/docs/reference/method/MongoDBClient-listDatabases.txt +++ b/docs/reference/method/MongoDBClient-listDatabases.txt @@ -58,7 +58,9 @@ The following example lists all databases on the server: var_dump($databaseInfo); } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\DatabaseInfo)#4 (3) { ["name"]=> diff --git a/docs/reference/method/MongoDBClient-selectCollection.txt b/docs/reference/method/MongoDBClient-selectCollection.txt index 6ce12df7b..88828c29b 100644 --- a/docs/reference/method/MongoDBClient-selectCollection.txt +++ b/docs/reference/method/MongoDBClient-selectCollection.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function selectCollection($databaseName, $collectionName, array $options = []): MongoDB\Collection + function selectCollection(string $databaseName, string $collectionName, array $options = []): MongoDB\Collection This method has the following parameters: @@ -72,7 +72,7 @@ with a custom read preference: 'test', 'users', [ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), + 'readPreference' => new MongoDB\Driver\ReadPreference('primaryPreferred'), ] ); diff --git a/docs/reference/method/MongoDBClient-selectDatabase.txt b/docs/reference/method/MongoDBClient-selectDatabase.txt index 10dc4d8ad..a556a06ae 100644 --- a/docs/reference/method/MongoDBClient-selectDatabase.txt +++ b/docs/reference/method/MongoDBClient-selectDatabase.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function selectDatabase($databaseName, array $options = []): MongoDB\Database + function selectDatabase(string $databaseName, array $options = []): MongoDB\Database This method has the following parameters: @@ -71,7 +71,7 @@ preference: $db = $client->selectDatabase( 'test', [ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), + 'readPreference' => new MongoDB\Driver\ReadPreference('primaryPreferred'), ] ); diff --git a/docs/reference/method/MongoDBClient-startSession.txt b/docs/reference/method/MongoDBClient-startSession.txt index c3fcce89a..07a9b2b20 100644 --- a/docs/reference/method/MongoDBClient-startSession.txt +++ b/docs/reference/method/MongoDBClient-startSession.txt @@ -53,7 +53,9 @@ The following example starts a new session: var_dump($session); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\Session)#2043 (4) { ["logicalSessionId"]=> diff --git a/docs/reference/method/MongoDBClient__construct.txt b/docs/reference/method/MongoDBClient__construct.txt index ad864d186..07694c91e 100644 --- a/docs/reference/method/MongoDBClient__construct.txt +++ b/docs/reference/method/MongoDBClient__construct.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function __construct($uri = 'mongodb://127.0.0.1/', array $uriOptions = [], array $driverOptions = []) + function __construct(?string $uri = null, array $uriOptions = [], array $driverOptions = []) This constructor has the following parameters: @@ -49,12 +49,27 @@ initialized on demand, when the first operation is executed. Examples -------- +.. start-connecting-include + +Connecting to a Standalone server +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you do not specify a ``$uri`` value, the driver connects to a standalone +:program:`mongod` on ``127.0.0.1`` via port ``27017``. To connect to a different +server, pass the corresponding connection string as the first parameter when +creating the :phpclass:`Client ` instance: + +.. code-block:: php + + ` documentation. +.. end-connecting-include + Specifying a Custom Type Map ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -113,7 +130,7 @@ By default, the |php-library| deserializes BSON documents and arrays as :phpclass:`MongoDB\\Model\\BSONDocument` and :phpclass:`MongoDB\\Model\\BSONArray` objects, respectively. The following example demonstrates how to have the library unserialize everything as a PHP -array, as was done in the legacy :php:`mongo extension `. +array, as was done in the legacy ``mongo`` extension. .. code-block:: php diff --git a/docs/reference/method/MongoDBClient__get.txt b/docs/reference/method/MongoDBClient__get.txt index c08245782..5e4097b2d 100644 --- a/docs/reference/method/MongoDBClient__get.txt +++ b/docs/reference/method/MongoDBClient__get.txt @@ -17,11 +17,11 @@ Definition Selects a database on the server. This :php:`magic method ` is an alias for the :phpmethod:`selectDatabase() - ` method. + ` method. .. code-block:: php - function __get($databaseName): MongoDB\Database + function __get(string $databaseName): MongoDB\Database This method has the following parameters: @@ -37,7 +37,7 @@ Behavior The selected database inherits options such as read preference and type mapping from the :phpclass:`Client ` object. If you wish to override -any options, use the :phpmethod:`MongoDB\\Client::selectDatabase` method. +any options, use the :phpmethod:`MongoDB\\Client::selectDatabase()` method. .. note:: diff --git a/docs/reference/method/MongoDBCollection-count.txt b/docs/reference/method/MongoDBCollection-count.txt index 0c5906304..1673ed91d 100644 --- a/docs/reference/method/MongoDBCollection-count.txt +++ b/docs/reference/method/MongoDBCollection-count.txt @@ -21,7 +21,7 @@ Definition .. code-block:: php - function count($filter = [], array $options = []): integer + function count(array|object $filter = [], array $options = []): integer This method has the following parameters: @@ -56,8 +56,7 @@ metadata. Even when provided with a query filter the ``count`` command can return inaccurate results with a sharded cluster if orphaned documents exist or if a chunk migration is in progress. The :phpmethod:`MongoDB\\Collection::countDocuments()` method avoids these sharded -cluster problems entirely when used with MongoDB 3.6+, and when a primary read -preference with older sharded clusters. +cluster problems entirely. .. include:: /includes/extracts/note-bson-comparison.rst diff --git a/docs/reference/method/MongoDBCollection-countDocuments.txt b/docs/reference/method/MongoDBCollection-countDocuments.txt index b66171c27..44b16c8e0 100644 --- a/docs/reference/method/MongoDBCollection-countDocuments.txt +++ b/docs/reference/method/MongoDBCollection-countDocuments.txt @@ -21,7 +21,7 @@ Definition .. code-block:: php - function countDocuments($filter = [], array $options = []): integer + function countDocuments(array|object $filter = [], array $options = []): integer This method has the following parameters: @@ -77,7 +77,7 @@ Consider the following alternatives to these restricted operators: - :query:`$geoWithin` with :query:`$centerSphere` * - :query:`$where` - - :query:`$expr` (requires MongoDB 3.6+) + - :query:`$expr` .. include:: /includes/extracts/note-bson-comparison.rst diff --git a/docs/reference/method/MongoDBCollection-createIndex.txt b/docs/reference/method/MongoDBCollection-createIndex.txt index bdae57123..ceef2e7bc 100644 --- a/docs/reference/method/MongoDBCollection-createIndex.txt +++ b/docs/reference/method/MongoDBCollection-createIndex.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function createIndex($key, array $options = []): string + function createIndex(array|object $key, array $options = []): string This method has the following parameters: @@ -66,7 +66,9 @@ the ``test`` database. var_dump($indexName); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(19) "borough_1_cuisine_1" @@ -95,7 +97,9 @@ exists. var_dump($indexName); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(9) "borough_1" diff --git a/docs/reference/method/MongoDBCollection-createIndexes.txt b/docs/reference/method/MongoDBCollection-createIndexes.txt index 05a3c0be3..76705e9ba 100644 --- a/docs/reference/method/MongoDBCollection-createIndexes.txt +++ b/docs/reference/method/MongoDBCollection-createIndexes.txt @@ -13,7 +13,7 @@ MongoDB\\Collection::createIndexes() Definition ---------- -.. phpmethod:: MongoDB\\Collection::createIndexes($indexes) +.. phpmethod:: MongoDB\\Collection::createIndexes() Create one or more indexes for the collection. @@ -53,7 +53,9 @@ fields that correspond to index options accepted by :phpmethod:`createIndex() For example, the following ``$indexes`` parameter creates two indexes. The first is an ascending unique index on the ``username`` field and the second is a -2dsphere index on the ``loc`` field with a custom name:: +2dsphere index on the ``loc`` field with a custom name: + +.. code-block:: none [ [ 'key' => [ 'username' => 1 ], 'unique' => true ], @@ -81,7 +83,9 @@ custom name. var_dump($indexNames); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(2) { [0]=> diff --git a/docs/reference/method/MongoDBCollection-deleteMany.txt b/docs/reference/method/MongoDBCollection-deleteMany.txt index df1a1ac81..29ad2e833 100644 --- a/docs/reference/method/MongoDBCollection-deleteMany.txt +++ b/docs/reference/method/MongoDBCollection-deleteMany.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function deleteMany($filter, array $options = []): MongoDB\DeleteResult + function deleteMany(array|object $filter, array $options = []): MongoDB\DeleteResult This method has the following parameters: @@ -68,7 +68,9 @@ that have ``"ny"`` as the value for the ``state`` field: printf("Deleted %d document(s)\n", $deleteResult->getDeletedCount()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Deleted 2 document(s) diff --git a/docs/reference/method/MongoDBCollection-deleteOne.txt b/docs/reference/method/MongoDBCollection-deleteOne.txt index 0c31bfb5f..6c888a961 100644 --- a/docs/reference/method/MongoDBCollection-deleteOne.txt +++ b/docs/reference/method/MongoDBCollection-deleteOne.txt @@ -21,7 +21,7 @@ Definition .. code-block:: php - function deleteOne($filter, array $options = []): MongoDB\DeleteResult + function deleteOne(array|object $filter, array $options = []): MongoDB\DeleteResult This method has the following parameters: @@ -70,7 +70,9 @@ has ``"ny"`` as the value for the ``state`` field: printf("Deleted %d document(s)\n", $deleteResult->getDeletedCount()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Deleted 1 document(s) diff --git a/docs/reference/method/MongoDBCollection-distinct.txt b/docs/reference/method/MongoDBCollection-distinct.txt index bf7ffb4c0..24fd6761a 100644 --- a/docs/reference/method/MongoDBCollection-distinct.txt +++ b/docs/reference/method/MongoDBCollection-distinct.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function distinct($fieldName, $filter = [], array $options = []): mixed[] + function distinct(string $fieldName, array|object $filter = [], array $options = []): mixed[] This method has the following parameters: @@ -66,7 +66,9 @@ in the ``restaurants`` collection in the ``test`` database. var_dump($distinct); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(6) { [0]=> @@ -100,7 +102,9 @@ the ``borough`` is ``Queens``: var_dump($distinct); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(75) { [0]=> diff --git a/docs/reference/method/MongoDBCollection-drop.txt b/docs/reference/method/MongoDBCollection-drop.txt index c1762871b..a2cd9410b 100644 --- a/docs/reference/method/MongoDBCollection-drop.txt +++ b/docs/reference/method/MongoDBCollection-drop.txt @@ -59,7 +59,9 @@ database: var_dump($result); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#9 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBCollection-dropIndex.txt b/docs/reference/method/MongoDBCollection-dropIndex.txt index 880349bdf..0a7fbb53f 100644 --- a/docs/reference/method/MongoDBCollection-dropIndex.txt +++ b/docs/reference/method/MongoDBCollection-dropIndex.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function dropIndex($indexName, array $options = []): array|object + function dropIndex(string|MongoDB\Model\IndexInfo $indexName, array $options = []): array|object This method has the following parameters: @@ -59,7 +59,9 @@ collection in the ``test`` database: var_dump($result); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#9 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBCollection-dropIndexes.txt b/docs/reference/method/MongoDBCollection-dropIndexes.txt index 8cdf9e43a..ee48d493c 100644 --- a/docs/reference/method/MongoDBCollection-dropIndexes.txt +++ b/docs/reference/method/MongoDBCollection-dropIndexes.txt @@ -60,7 +60,9 @@ The following drops all indexes from the ``restaurants`` collection in the var_dump($result); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#9 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBCollection-estimatedDocumentCount.txt b/docs/reference/method/MongoDBCollection-estimatedDocumentCount.txt index 097e03b41..1a8316293 100644 --- a/docs/reference/method/MongoDBCollection-estimatedDocumentCount.txt +++ b/docs/reference/method/MongoDBCollection-estimatedDocumentCount.txt @@ -50,7 +50,15 @@ Behavior This method returns an estimate of the count of documents in the collection using collection metadata, rather than counting the documents or consulting an index. This method does not take a ``session`` option and cannot be executed -within a transaction. +within a transaction. See +`Count: Behavior `_ +in the MongoDB manual for more information. + +This method is implemented using the :manual:`count ` +command. Due to an oversight in versions 5.0.0-5.0.8 of MongoDB, the ``count`` +command was not included in version "1" of the Stable API. Applications using +this method with the Stable API are recommended to upgrade their server version +to 5.0.9+ or disable strict mode to avoid encountering errors. See Also -------- diff --git a/docs/reference/method/MongoDBCollection-explain.txt b/docs/reference/method/MongoDBCollection-explain.txt index 5ae73c911..600f203b8 100644 --- a/docs/reference/method/MongoDBCollection-explain.txt +++ b/docs/reference/method/MongoDBCollection-explain.txt @@ -21,7 +21,7 @@ Definition .. code-block:: php - function explain(MongoDB\\Operation\\Explainable $explainable, array $options = []): array|object + function explain(MongoDB\Operation\Explainable $explainable, array $options = []): array|object This method has the following parameters: @@ -50,18 +50,18 @@ Explainable Commands Explainable commands include, but are not limited to: - - :phpclass:`MongoDB\\Operation\\Aggregate` - - :phpclass:`MongoDB\\Operation\\Count` - - :phpclass:`MongoDB\\Operation\\DeleteMany` - - :phpclass:`MongoDB\\Operation\\DeleteOne` - - :phpclass:`MongoDB\\Operation\\Distinct` - - :phpclass:`MongoDB\\Operation\\Find` - - :phpclass:`MongoDB\\Operation\\FindOne` - - :phpclass:`MongoDB\\Operation\\FindOneAndDelete` - - :phpclass:`MongoDB\\Operation\\FindOneAndReplace` - - :phpclass:`MongoDB\\Operation\\FindOneAndUpdate` - - :phpclass:`MongoDB\\Operation\\UpdateMany` - - :phpclass:`MongoDB\\Operation\\UpdateOne` +- ``MongoDB\Operation\Aggregate`` +- ``MongoDB\Operation\Count`` +- ``MongoDB\Operation\DeleteMany`` +- ``MongoDB\Operation\DeleteOne`` +- ``MongoDB\Operation\Distinct`` +- ``MongoDB\Operation\Find`` +- ``MongoDB\Operation\FindOne`` +- ``MongoDB\Operation\FindOneAndDelete`` +- ``MongoDB\Operation\FindOneAndReplace`` +- ``MongoDB\Operation\FindOneAndUpdate`` +- ``MongoDB\Operation\UpdateMany`` +- ``MongoDB\Operation\UpdateOne`` Examples -------- @@ -84,7 +84,9 @@ This example explains a count command. var_dump($result); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#29 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBCollection-find.txt b/docs/reference/method/MongoDBCollection-find.txt index 34eed99ad..4fe923893 100644 --- a/docs/reference/method/MongoDBCollection-find.txt +++ b/docs/reference/method/MongoDBCollection-find.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function find($filter = [], array $options = []): MongoDB\Driver\Cursor + function find(array|object $filter = [], array $options = []): MongoDB\Driver\Cursor This method has the following parameters: @@ -56,6 +56,8 @@ returned. It also limits the results to 5 documents. .. code-block:: php + test->restaurants; $cursor = $collection->find( @@ -77,7 +79,9 @@ returned. It also limits the results to 5 documents. var_dump($restaurant); }; -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#10 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBCollection-findOne.txt b/docs/reference/method/MongoDBCollection-findOne.txt index 0bf95f648..44829db30 100644 --- a/docs/reference/method/MongoDBCollection-findOne.txt +++ b/docs/reference/method/MongoDBCollection-findOne.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function findOne($filter = [], array $options = []): array|object|null + function findOne(array|object $filter = [], array $options = []): array|object|null This method has the following parameters: @@ -62,6 +62,8 @@ special BSON type, the query criteria for selecting a restaurant must use the .. code-block:: php + test; $zip = $database->zips->findOne(['_id' => '10036']); @@ -80,6 +82,8 @@ returned. .. code-block:: php + test->restaurants; $restaurant = $collection->findOne( @@ -98,7 +102,9 @@ returned. var_dump($restaurant); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#10 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBCollection-findOneAndDelete.txt b/docs/reference/method/MongoDBCollection-findOneAndDelete.txt index 1253bad40..e5531f416 100644 --- a/docs/reference/method/MongoDBCollection-findOneAndDelete.txt +++ b/docs/reference/method/MongoDBCollection-findOneAndDelete.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function findOneAndDelete($filter = [], array $options = []): object|null + function findOneAndDelete(array|object $filter = [], array $options = []): object|null This method has the following parameters: @@ -73,7 +73,9 @@ The following example finds and deletes the document with ``restaurant_id`` of var_dump($deletedRestaurant); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#17 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBCollection-findOneAndReplace.txt b/docs/reference/method/MongoDBCollection-findOneAndReplace.txt index 6a96a30b0..66bd7f12f 100644 --- a/docs/reference/method/MongoDBCollection-findOneAndReplace.txt +++ b/docs/reference/method/MongoDBCollection-findOneAndReplace.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function findOneAndReplace($filter, $replacement, array $options = []): object|null + function findOneAndReplace(array|object $filter, array|object $replacement, array $options = []): object|null This method has the following parameters: @@ -115,7 +115,9 @@ The following operation replaces the document with ``restaurant_id`` of var_dump($replacedRestaurant); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#18 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBCollection-findOneAndUpdate.txt b/docs/reference/method/MongoDBCollection-findOneAndUpdate.txt index 377d255ed..c903698b1 100644 --- a/docs/reference/method/MongoDBCollection-findOneAndUpdate.txt +++ b/docs/reference/method/MongoDBCollection-findOneAndUpdate.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function findOneAndUpdate($filter, $update, array $options = []): object|null + function findOneAndUpdate(array|object $filter, array|object $update, array $options = []): object|null This method has the following parameters: @@ -74,7 +74,9 @@ setting its building number to ``"761"``: var_dump($updatedRestaurant); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#20 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBCollection-getCollectionName.txt b/docs/reference/method/MongoDBCollection-getCollectionName.txt index 86fe3e5ec..70be8a8a1 100644 --- a/docs/reference/method/MongoDBCollection-getCollectionName.txt +++ b/docs/reference/method/MongoDBCollection-getCollectionName.txt @@ -40,7 +40,9 @@ The following returns the collection name for the ``zips`` collection in the echo $collection->getCollectionName(); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none zips diff --git a/docs/reference/method/MongoDBCollection-getDatabaseName.txt b/docs/reference/method/MongoDBCollection-getDatabaseName.txt index 93500c809..f83632bac 100644 --- a/docs/reference/method/MongoDBCollection-getDatabaseName.txt +++ b/docs/reference/method/MongoDBCollection-getDatabaseName.txt @@ -40,7 +40,9 @@ The following returns the database name for the ``zips`` collection in the echo $collection->getDatabaseName(); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none test diff --git a/docs/reference/method/MongoDBCollection-getNamespace.txt b/docs/reference/method/MongoDBCollection-getNamespace.txt index 74f1b022e..78e2484be 100644 --- a/docs/reference/method/MongoDBCollection-getNamespace.txt +++ b/docs/reference/method/MongoDBCollection-getNamespace.txt @@ -41,7 +41,9 @@ database. echo $collection->getNamespace(); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none test.zips diff --git a/docs/reference/method/MongoDBCollection-getReadConcern.txt b/docs/reference/method/MongoDBCollection-getReadConcern.txt index ff1cfeb04..37873d0f9 100644 --- a/docs/reference/method/MongoDBCollection-getReadConcern.txt +++ b/docs/reference/method/MongoDBCollection-getReadConcern.txt @@ -36,12 +36,14 @@ Example selectCollection('test', 'users', [ - 'readConcern' => new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::MAJORITY), + 'readConcern' => new MongoDB\Driver\ReadConcern('majority'), ]); var_dump($collection->getReadConcern()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\ReadConcern)#5 (1) { ["level"]=> diff --git a/docs/reference/method/MongoDBCollection-getReadPreference.txt b/docs/reference/method/MongoDBCollection-getReadPreference.txt index 4eac30d3a..622701e55 100644 --- a/docs/reference/method/MongoDBCollection-getReadPreference.txt +++ b/docs/reference/method/MongoDBCollection-getReadPreference.txt @@ -42,7 +42,9 @@ Example var_dump($collection->getReadPreference()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\ReadPreference)#5 (1) { ["mode"]=> diff --git a/docs/reference/method/MongoDBCollection-getTypeMap.txt b/docs/reference/method/MongoDBCollection-getTypeMap.txt index 11e56426a..50cd91dad 100644 --- a/docs/reference/method/MongoDBCollection-getTypeMap.txt +++ b/docs/reference/method/MongoDBCollection-getTypeMap.txt @@ -45,7 +45,9 @@ Example var_dump($collection->getTypeMap()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(3) { ["root"]=> diff --git a/docs/reference/method/MongoDBCollection-getWriteConcern.txt b/docs/reference/method/MongoDBCollection-getWriteConcern.txt index 4fa26ee1f..a7ad354db 100644 --- a/docs/reference/method/MongoDBCollection-getWriteConcern.txt +++ b/docs/reference/method/MongoDBCollection-getWriteConcern.txt @@ -42,7 +42,9 @@ Example var_dump($collection->getWriteConcern()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\WriteConcern)#5 (2) { ["w"]=> diff --git a/docs/reference/method/MongoDBCollection-insertMany.txt b/docs/reference/method/MongoDBCollection-insertMany.txt index 8b7eb53b0..62b0858e6 100644 --- a/docs/reference/method/MongoDBCollection-insertMany.txt +++ b/docs/reference/method/MongoDBCollection-insertMany.txt @@ -79,7 +79,9 @@ in the ``test`` database: var_dump($insertManyResult->getInsertedIds()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Inserted 2 document(s) array(2) { diff --git a/docs/reference/method/MongoDBCollection-insertOne.txt b/docs/reference/method/MongoDBCollection-insertOne.txt index 0d25f6d51..dd6e2f220 100644 --- a/docs/reference/method/MongoDBCollection-insertOne.txt +++ b/docs/reference/method/MongoDBCollection-insertOne.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function insertOne($document, array $options = []): MongoDB\InsertOneResult + function insertOne(array|object $document, array $options = []): MongoDB\InsertOneResult This method has the following parameters: @@ -71,7 +71,9 @@ The following operation inserts a document into the ``users`` collection in the var_dump($insertOneResult->getInsertedId()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Inserted 1 document(s) object(MongoDB\BSON\ObjectId)#11 (1) { diff --git a/docs/reference/method/MongoDBCollection-listIndexes.txt b/docs/reference/method/MongoDBCollection-listIndexes.txt index e39a9bbba..2508b2828 100644 --- a/docs/reference/method/MongoDBCollection-listIndexes.txt +++ b/docs/reference/method/MongoDBCollection-listIndexes.txt @@ -57,7 +57,9 @@ collection in the ``test`` database: var_dump($index); } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\IndexInfo)#8 (4) { ["v"]=> diff --git a/docs/reference/method/MongoDBCollection-mapReduce.txt b/docs/reference/method/MongoDBCollection-mapReduce.txt index 2796e3857..a38872bdc 100644 --- a/docs/reference/method/MongoDBCollection-mapReduce.txt +++ b/docs/reference/method/MongoDBCollection-mapReduce.txt @@ -2,6 +2,8 @@ MongoDB\\Collection::mapReduce() ================================= +.. deprecated:: 1.12 + .. versionadded:: 1.2 .. default-domain:: mongodb @@ -22,7 +24,7 @@ Definition .. code-block:: php - function mapReduce($map, $reduce, $out, array $options = []): MongoDB\MapReduceResult + function mapReduce(MongoDB\BSON\JavascriptInterface $map, MongoDB\BSON\JavascriptInterface $reduce, string|array|object $out, array $options = []): MongoDB\MapReduceResult This method has the following parameters: @@ -90,7 +92,9 @@ each state. var_dump($pop); }; -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(stdClass)#2293 (2) { ["_id"]=> diff --git a/docs/reference/method/MongoDBCollection-rename.txt b/docs/reference/method/MongoDBCollection-rename.txt new file mode 100644 index 000000000..a40555f21 --- /dev/null +++ b/docs/reference/method/MongoDBCollection-rename.txt @@ -0,0 +1,81 @@ +============================= +MongoDB\\Collection::rename() +============================= + +.. versionadded:: 1.10 + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Definition +---------- + +.. phpmethod:: MongoDB\\Collection::rename() + + Rename the collection. + + .. code-block:: php + + function rename(string $toCollectionName, ?string $toDatabaseName = null, array $options = []): array|object + + This method has the following parameters: + + .. include:: /includes/apiargs/MongoDBCollection-method-rename-param.rst + + The ``$options`` parameter supports the following options: + + .. include:: /includes/apiargs/MongoDBCollection-method-rename-option.rst + +Return Values +------------- + +An array or object with the result document of the :manual:`renameCollection +` command. The return type will depend on the +``typeMap`` option. + +Errors/Exceptions +----------------- + +.. include:: /includes/extracts/error-unsupportedexception.rst +.. include:: /includes/extracts/error-invalidargumentexception.rst +.. include:: /includes/extracts/error-driver-runtimeexception.rst + +Example +------- + +The following operation renames the ``restaurants`` collection in the ``test`` +database to ``places``: + +.. code-block:: php + + test->restaurants; + + $result = $collection->rename('places'); + + var_dump($result); + +The output would then resemble: + +.. code-block:: none + + object(MongoDB\Model\BSONDocument)#9 (1) { + ["storage":"ArrayObject":private]=> + array(1) { + ["ok"]=> + float(1) + } + } + +See Also +-------- + +- :phpmethod:`MongoDB\\Database::renameCollection()` +- :manual:`renameCollection ` command reference in the MongoDB + manual diff --git a/docs/reference/method/MongoDBCollection-replaceOne.txt b/docs/reference/method/MongoDBCollection-replaceOne.txt index ba43452b8..e42f993e9 100644 --- a/docs/reference/method/MongoDBCollection-replaceOne.txt +++ b/docs/reference/method/MongoDBCollection-replaceOne.txt @@ -21,7 +21,7 @@ Definition .. code-block:: php - function replaceOne($filter, $replacement, array $options = []): MongoDB\UpdateResult + function replaceOne(array|object $filter, array|object $replacement, array $options = []): MongoDB\UpdateResult This method has the following parameters: @@ -77,7 +77,9 @@ The following example replaces the document with ``restaurant_id`` of printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Matched 1 document(s) Modified 1 document(s) diff --git a/docs/reference/method/MongoDBCollection-updateMany.txt b/docs/reference/method/MongoDBCollection-updateMany.txt index bd72fdf9c..0e5f22b9d 100644 --- a/docs/reference/method/MongoDBCollection-updateMany.txt +++ b/docs/reference/method/MongoDBCollection-updateMany.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function updateMany($filter, $update, array $options = []): MongoDB\UpdateResult + function updateMany(array|object $filter, array|object $update, array $options = []): MongoDB\UpdateResult This method has the following parameters: @@ -57,17 +57,21 @@ The following example updates all of the documents with the ``borough`` of .. code-block:: php + test->restaurants; $updateResult = $collection->updateMany( [ 'borough' => 'Queens' ], - [ '$set' => [ 'active' => 'True' ]] + [ '$set' => [ 'active' => true ]] ); printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Matched 5656 document(s) Modified 5656 document(s) diff --git a/docs/reference/method/MongoDBCollection-updateOne.txt b/docs/reference/method/MongoDBCollection-updateOne.txt index bed657f92..2b930e232 100644 --- a/docs/reference/method/MongoDBCollection-updateOne.txt +++ b/docs/reference/method/MongoDBCollection-updateOne.txt @@ -21,7 +21,7 @@ Definition .. code-block:: php - function updateOne($filter, $update, array $options = []): MongoDB\UpdateResult + function updateOne(array|object $filter, array|object $update, array $options = []): MongoDB\UpdateResult This method has the following parameters: @@ -59,6 +59,8 @@ The following example updates one document with the ``restaurant_id`` of .. code-block:: php + test->restaurants; $updateResult = $collection->updateOne( @@ -69,7 +71,9 @@ The following example updates one document with the ``restaurant_id`` of printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Matched 1 document(s) Modified 1 document(s) diff --git a/docs/reference/method/MongoDBCollection-withOptions.txt b/docs/reference/method/MongoDBCollection-withOptions.txt index 92e09f6e3..a68a5fec0 100644 --- a/docs/reference/method/MongoDBCollection-withOptions.txt +++ b/docs/reference/method/MongoDBCollection-withOptions.txt @@ -52,7 +52,7 @@ preference: $collection = (new MongoDB\Client)->selectCollection('test', 'restaurants'); $newCollection = $sourceCollection->withOptions([ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), + 'readPreference' => new MongoDB\Driver\ReadPreference('primaryPreferred'), ]); See Also diff --git a/docs/reference/method/MongoDBCollection__construct.txt b/docs/reference/method/MongoDBCollection__construct.txt index c3ef0c6bf..e8077c73f 100644 --- a/docs/reference/method/MongoDBCollection__construct.txt +++ b/docs/reference/method/MongoDBCollection__construct.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function __construct(MongoDB\Driver\Manager $manager, $databaseName, $collectionName, array $options = []) + function __construct(MongoDB\Driver\Manager $manager, string $databaseName, string $collectionName, array $options = []) This constructor has the following parameters: diff --git a/docs/reference/method/MongoDBDatabase-command.txt b/docs/reference/method/MongoDBDatabase-command.txt index 46b28fbee..6853daad8 100644 --- a/docs/reference/method/MongoDBDatabase-command.txt +++ b/docs/reference/method/MongoDBDatabase-command.txt @@ -15,11 +15,13 @@ Definition .. phpmethod:: MongoDB\\Database::command() - Execute a :manual:`command ` on the database. + Execute a :manual:`command ` on the database. This is + generally used to execute commands that do not have a corresponding helper + method within the library. .. code-block:: php - function command($command, array $options = []): MongoDB\Driver\Cursor + function command(array|object $command, array $options = []): MongoDB\Driver\Cursor This method has the following parameters: @@ -43,9 +45,10 @@ Errors/Exceptions Example ------- -The following example executes an :manual:`isMaster -` command, which returns a cursor with a single -result document: +Most database commands return a single result document, which can be obtained by +converting the returned cursor to an array and accessing its first element. The +following example executes a :manual:`ping ` command +and prints its result document: .. code-block:: php @@ -53,40 +56,27 @@ result document: $database = (new MongoDB\Client)->test; - $cursor = $database->command(['isMaster' => 1]); + $cursor = $database->command(['ping' => 1]); - var_dump($c->toArray()[0]); + var_dump($cursor->toArray()[0]); -The output would resemble:: +The output would resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#11 (1) { ["storage":"ArrayObject":private]=> - array(8) { - ["ismaster"]=> - bool(true) - ["maxBsonObjectSize"]=> - int(16777216) - ["maxMessageSizeBytes"]=> - int(48000000) - ["maxWriteBatchSize"]=> - int(1000) - ["localTime"]=> - object(MongoDB\BSON\UTCDateTime)#3 (1) { - ["milliseconds"]=> - string(13) "1477608046464" - } - ["maxWireVersion"]=> - int(4) - ["minWireVersion"]=> - int(0) + array(1) { ["ok"]=> float(1) } } -The following example executes a :manual:`listCollections -` command, which returns a cursor with -multiple result documents: +Some database commands return a cursor with multiple results. The following +example executes :manual:`listCollections `, +which returns a cursor containing a result document for each collection in the +``test`` database. Note that this example is illustrative; applications would +generally use :phpmethod:`MongoDB\\Database::listCollections()` in practice. .. code-block:: php @@ -94,11 +84,13 @@ multiple result documents: $database = (new MongoDB\Client)->test; - $cursor = $database->command(['isMaster' => 1]); + $cursor = $database->command(['listCollections' => 1]); + + var_dump($cursor->toArray()); - var_dump($c->toArray()); +The output would resemble: -The output would resemble:: +.. code-block:: none array(3) { [0]=> diff --git a/docs/reference/method/MongoDBDatabase-createCollection.txt b/docs/reference/method/MongoDBDatabase-createCollection.txt index 8db6696c4..56129b975 100644 --- a/docs/reference/method/MongoDBDatabase-createCollection.txt +++ b/docs/reference/method/MongoDBDatabase-createCollection.txt @@ -19,14 +19,14 @@ Definition .. code-block:: php - function createCollection($collectionName, array $options = []): array|object + function createCollection(string $collectionName, array $options = []): array|object MongoDB creates collections implicitly when you first reference the collection in a command, such as when inserting a document into a new collection. You may also explicitly create a collection with specific options using the :phpmethod:`MongoDB\\Database::createCollection()` method, or using :manual:`db.createCollection() ` in - the :program:`mongo` shell. + the MongoDB shell. Explicitly creating collections enables you to create :manual:`capped collections `, specify @@ -41,11 +41,9 @@ Definition .. include:: /includes/apiargs/MongoDBDatabase-method-createCollection-option.rst - Note that not all options are available on all versions of MongoDB. Document - validation, for example, was added in MongoDB 3.2; similarly, the WiredTiger - storage engine is available only for MongoDB 3.0 and later. Refer to the - :manual:`create ` command reference in the MongoDB - manual for compatibility considerations. + Note that not all options are available on all versions of MongoDB. Refer to + the :manual:`create ` command reference in the + MongoDB manual for compatibility considerations. Return Values ------------- @@ -81,7 +79,9 @@ database with document validation criteria: var_dump($result); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#11 (1) { ["storage":"ArrayObject":private]=> @@ -97,3 +97,4 @@ See Also - :manual:`create ` command reference in the MongoDB manual - :manual:`db.createCollection() ` +- :manual:`Time Series Collections ` diff --git a/docs/reference/method/MongoDBDatabase-createEncryptedCollection.txt b/docs/reference/method/MongoDBDatabase-createEncryptedCollection.txt new file mode 100644 index 000000000..64c643f03 --- /dev/null +++ b/docs/reference/method/MongoDBDatabase-createEncryptedCollection.txt @@ -0,0 +1,136 @@ +============================================== +MongoDB\\Database::createEncryptedCollection() +============================================== + +.. versionadded:: 1.16 + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Definition +---------- + +.. phpmethod:: MongoDB\\Database::createEncryptedCollection() + + Explicitly creates an encrypted collection. + + .. code-block:: php + + function createEncryptedCollection(string $collectionName, MongoDB\Driver\ClientEncryption $clientEncryption, string $kmsProvider, ?array $masterKey, array $options): array + + This method will automatically create data keys for any encrypted fields + where ``keyId`` is ``null``. Data keys will be created using + :php:`MongoDB\\Driver\\ClientEncryption::createDataKey() ` + and the provided ``$kmsProvider`` and ``$masterKey`` parameters. A copy of + the modified ``encryptedFields`` option will be returned in addition to the + result from creating the collection. + + This method does not affect any auto encryption settings on existing + :phpclass:`MongoDB\\Client` objects. Users must configure auto encryption + after creating the encrypted collection with ``createEncryptedCollection()``. + + This method has the following parameters: + + .. include:: /includes/apiargs/MongoDBDatabase-method-createEncryptedCollection-param.rst + + The ``$options`` parameter supports the same options as + :phpmethod:`MongoDB\\Database::createCollection()`. The ``encryptedFields`` + option is required. + +Return Values +------------- + +A tuple (i.e. two-element array) containing the result document from the +:manual:`create ` command (an array or object +according to the ``typeMap`` option) and the modified ``encryptedFields`` +option. + +Errors/Exceptions +----------------- + +:phpclass:`MongoDB\\Exception\\CreateEncryptedCollectionException` if any error +is encountered creating data keys or the collection. The original exception and +modified ``encryptedFields`` option can be accessed via the ``getPrevious()`` +and ``getEncryptedFields()`` methods, respectively. + +.. include:: /includes/extracts/error-invalidargumentexception.rst + +Example +------- + +The following example creates an encrypted ``users`` collection in the ``test`` +database. The ``ssn`` field within the ``users`` collection will be defined as +an encrypted string field. + +.. code-block:: php + + createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'local' => ['key' => new MongoDB\BSON\Binary(base64_decode(LOCAL_MASTERKEY), 0)], + ], + ); + + [$result, $encryptedFields] = $client->test->createEncryptedCollection( + 'users', + $clientEncryption, + 'local', + null, + [ + 'encryptedFields' => [ + 'fields' => [ + ['path' => 'ssn', 'bsonType' => 'string', 'keyId' => null], + ], + ], + ] + ); + +If the encrypted collection was successfully created, ``$result`` will contain +the response document from the ``create`` command and +``$encryptedFields['fields'][0]['keyId']`` will contain a +:php:`MongoDB\\BSON\\Binary ` object with subtype 4 +(i.e. UUID). + +The modified ``encryptedFields`` option can then be used to construct a new +:phpclass:`MongoDB\\Client` with auto encryption enabled. + +.. code-block:: php + + [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'local' => ['key' => new MongoDB\BSON\Binary(base64_decode(LOCAL_MASTERKEY), 0)], + ], + 'encryptedFieldsMap' => [ + 'test.users' => $encryptedFields, + ], + ], + ] + ); + +See Also +-------- + +- :phpmethod:`MongoDB\\Database::createCollection()` +- :phpmethod:`MongoDB\\Client::createClientEncryption()` +- :php:`MongoDB\\Driver\\ClientEncryption::createDataKey() ` +- :manual:`create ` command reference in the MongoDB + manual diff --git a/docs/reference/method/MongoDBDatabase-drop.txt b/docs/reference/method/MongoDBDatabase-drop.txt index bcf6ca88a..eae1bb6b8 100644 --- a/docs/reference/method/MongoDBDatabase-drop.txt +++ b/docs/reference/method/MongoDBDatabase-drop.txt @@ -58,7 +58,9 @@ The following example drops the ``test`` database: var_dump($result); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#8 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBDatabase-dropCollection.txt b/docs/reference/method/MongoDBDatabase-dropCollection.txt index fbc005b86..028e63f00 100644 --- a/docs/reference/method/MongoDBDatabase-dropCollection.txt +++ b/docs/reference/method/MongoDBDatabase-dropCollection.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function dropCollection($collectionName, array $options = []): array|object + function dropCollection(string $collectionName, array $options = []): array|object This method has the following parameters: @@ -58,7 +58,9 @@ The following example drops the ``users`` collection in the ``test`` database: var_dump($result); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#8 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBDatabase-getDatabaseName.txt b/docs/reference/method/MongoDBDatabase-getDatabaseName.txt index 6ff9dcb96..6e3abd966 100644 --- a/docs/reference/method/MongoDBDatabase-getDatabaseName.txt +++ b/docs/reference/method/MongoDBDatabase-getDatabaseName.txt @@ -39,6 +39,8 @@ The following example prints the name of a database object: echo $db->getDatabaseName(); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none test diff --git a/docs/reference/method/MongoDBDatabase-getReadConcern.txt b/docs/reference/method/MongoDBDatabase-getReadConcern.txt index fa277d884..56568aa4b 100644 --- a/docs/reference/method/MongoDBDatabase-getReadConcern.txt +++ b/docs/reference/method/MongoDBDatabase-getReadConcern.txt @@ -36,12 +36,14 @@ Example selectDatabase('test', [ - 'readConcern' => new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::MAJORITY), + 'readConcern' => new MongoDB\Driver\ReadConcern('majority'), ]); var_dump($database->getReadConcern()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\ReadConcern)#5 (1) { ["level"]=> diff --git a/docs/reference/method/MongoDBDatabase-getReadPreference.txt b/docs/reference/method/MongoDBDatabase-getReadPreference.txt index c70b40268..5001455c7 100644 --- a/docs/reference/method/MongoDBDatabase-getReadPreference.txt +++ b/docs/reference/method/MongoDBDatabase-getReadPreference.txt @@ -42,7 +42,9 @@ Example var_dump($database->getReadPreference()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\ReadPreference)#5 (1) { ["mode"]=> diff --git a/docs/reference/method/MongoDBDatabase-getTypeMap.txt b/docs/reference/method/MongoDBDatabase-getTypeMap.txt index bc1688d63..d06c67779 100644 --- a/docs/reference/method/MongoDBDatabase-getTypeMap.txt +++ b/docs/reference/method/MongoDBDatabase-getTypeMap.txt @@ -45,7 +45,9 @@ Example var_dump($database->getTypeMap()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(3) { ["root"]=> diff --git a/docs/reference/method/MongoDBDatabase-getWriteConcern.txt b/docs/reference/method/MongoDBDatabase-getWriteConcern.txt index 31b0ded51..55ea09f4d 100644 --- a/docs/reference/method/MongoDBDatabase-getWriteConcern.txt +++ b/docs/reference/method/MongoDBDatabase-getWriteConcern.txt @@ -42,7 +42,9 @@ Example var_dump($database->getWriteConcern()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\WriteConcern)#5 (2) { ["w"]=> diff --git a/docs/reference/method/MongoDBDatabase-listCollectionNames.txt b/docs/reference/method/MongoDBDatabase-listCollectionNames.txt index 6e973df19..b6eaa4ce5 100644 --- a/docs/reference/method/MongoDBDatabase-listCollectionNames.txt +++ b/docs/reference/method/MongoDBDatabase-listCollectionNames.txt @@ -52,7 +52,9 @@ The following example lists all of the collections in the ``test`` database: var_dump($collectionName); } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(11) "restaurants" string(5) "users" @@ -77,7 +79,9 @@ in the ``test`` database: var_dump($collectionName); } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(11) "restaurants" string(6) "restos" @@ -91,7 +95,7 @@ See Also -------- - :phpmethod:`MongoDB\\Database::listCollections()` -- :manual:`listCollections ` command reference in the MongoDB manual - `Enumerating Collections `_ diff --git a/docs/reference/method/MongoDBDatabase-listCollections.txt b/docs/reference/method/MongoDBDatabase-listCollections.txt index eb0c4c921..421b6cab4 100644 --- a/docs/reference/method/MongoDBDatabase-listCollections.txt +++ b/docs/reference/method/MongoDBDatabase-listCollections.txt @@ -51,7 +51,9 @@ The following example lists all of the collections in the ``test`` database: var_dump($collectionInfo); } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\CollectionInfo)#3 (2) { ["name"]=> @@ -94,7 +96,9 @@ in the ``test`` database: var_dump($collectionInfo); } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\CollectionInfo)#3 (2) { ["name"]=> @@ -115,7 +119,7 @@ See Also -------- - :phpmethod:`MongoDB\\Database::listCollectionNames()` -- :manual:`listCollections ` command reference in the MongoDB manual - `Enumerating Collections `_ diff --git a/docs/reference/method/MongoDBDatabase-modifyCollection.txt b/docs/reference/method/MongoDBDatabase-modifyCollection.txt index 43eb2d259..86c1ff523 100644 --- a/docs/reference/method/MongoDBDatabase-modifyCollection.txt +++ b/docs/reference/method/MongoDBDatabase-modifyCollection.txt @@ -22,7 +22,7 @@ Definition .. code-block:: php - function modifyCollection($collectionName, array $collectionOptions, array $options = []): array|object + function modifyCollection(string $collectionName, array $collectionOptions, array $options = []): array|object This method has the following parameters: @@ -63,7 +63,9 @@ The following example changes the expiration time of a TTL collection in the var_dump($result); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(stdClass)#2779 { ["expireAfterSeconds_old"]=> diff --git a/docs/reference/method/MongoDBDatabase-renameCollection.txt b/docs/reference/method/MongoDBDatabase-renameCollection.txt new file mode 100644 index 000000000..8bbe5b8e9 --- /dev/null +++ b/docs/reference/method/MongoDBDatabase-renameCollection.txt @@ -0,0 +1,81 @@ +===================================== +MongoDB\\Database::renameCollection() +===================================== + +.. versionadded:: 1.10 + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Definition +---------- + +.. phpmethod:: MongoDB\\Database::renameCollection() + + Rename a collection within the current database. + + .. code-block:: php + + function renameCollection(string $fromCollectionName, string $toCollectionName, ?string $toDatabaseName = null, array $options = []): array|object + + This method has the following parameters: + + .. include:: /includes/apiargs/MongoDBDatabase-method-renameCollection-param.rst + + The ``$options`` parameter supports the following options: + + .. include:: /includes/apiargs/MongoDBDatabase-method-renameCollection-option.rst + +Return Values +------------- + +An array or object with the result document of the :manual:`renameCollection +` command. The return type will depend on the +``typeMap`` option. + +Errors/Exceptions +----------------- + +.. include:: /includes/extracts/error-unsupportedexception.rst +.. include:: /includes/extracts/error-invalidargumentexception.rst +.. include:: /includes/extracts/error-driver-runtimeexception.rst + +Example +------- + +The following example renames the ``restaurants`` collection in the ``test`` +database to ``places``: + +.. code-block:: php + + test; + + $result = $db->renameCollection('restaurants', 'places'); + + var_dump($result); + +The output would then resemble: + +.. code-block:: none + + object(MongoDB\Model\BSONDocument)#8 (1) { + ["storage":"ArrayObject":private]=> + array(1) { + ["ok"]=> + float(1) + } + } + +See Also +-------- + +- :phpmethod:`MongoDB\\Collection::rename()` +- :manual:`renameCollection ` command reference in the MongoDB + manual diff --git a/docs/reference/method/MongoDBDatabase-selectCollection.txt b/docs/reference/method/MongoDBDatabase-selectCollection.txt index 6294e1a75..148854100 100644 --- a/docs/reference/method/MongoDBDatabase-selectCollection.txt +++ b/docs/reference/method/MongoDBDatabase-selectCollection.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function selectCollection($collectionName, array $options = []): MongoDB\Collection + function selectCollection(string $collectionName, array $options = []): MongoDB\Collection This method has the following parameters: @@ -71,7 +71,7 @@ database with a custom read preference: $users = $db->selectCollection( 'users', [ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), + 'readPreference' => new MongoDB\Driver\ReadPreference('primaryPreferred'), ] ); diff --git a/docs/reference/method/MongoDBDatabase-selectGridFSBucket.txt b/docs/reference/method/MongoDBDatabase-selectGridFSBucket.txt index 9c63fdf7c..f93906276 100644 --- a/docs/reference/method/MongoDBDatabase-selectGridFSBucket.txt +++ b/docs/reference/method/MongoDBDatabase-selectGridFSBucket.txt @@ -71,7 +71,7 @@ database with a custom read preference: $imagesBucket = $db->selectGridFSBucket([ 'bucketName' => 'images', - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), + 'readPreference' => new MongoDB\Driver\ReadPreference('primaryPreferred'), ]); See Also diff --git a/docs/reference/method/MongoDBDatabase-withOptions.txt b/docs/reference/method/MongoDBDatabase-withOptions.txt index 9bc0a82ea..6f732eacc 100644 --- a/docs/reference/method/MongoDBDatabase-withOptions.txt +++ b/docs/reference/method/MongoDBDatabase-withOptions.txt @@ -52,7 +52,7 @@ preference: $db = (new MongoDB\Client)->test; $newDb = $db->withOptions([ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), + 'readPreference' => new MongoDB\Driver\ReadPreference('primaryPreferred'), ]); See Also diff --git a/docs/reference/method/MongoDBDatabase__get.txt b/docs/reference/method/MongoDBDatabase__get.txt index c41e3981d..53cef6f9d 100644 --- a/docs/reference/method/MongoDBDatabase__get.txt +++ b/docs/reference/method/MongoDBDatabase__get.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function __get($collectionName): MongoDB\Collection + function __get(string $collectionName): MongoDB\Collection This method has the following parameters: @@ -35,7 +35,7 @@ Behavior The selected collection inherits options such as read preference and type mapping from the :phpclass:`Database ` object. If you wish to -override any options, use the :phpmethod:`MongoDB\\Database::selectCollection` +override any options, use the :phpmethod:`MongoDB\\Database::selectCollection()` method. .. note:: @@ -43,7 +43,7 @@ method. To select collections whose names contain special characters, such as ``.``, use complex syntax, as in ``$database->{'that.database'}``. - Alternatively, :phpmethod:`MongoDB\\Database::selectCollection` supports + Alternatively, :phpmethod:`MongoDB\\Database::selectCollection()` supports selecting collections whose names contain special characters. Examples diff --git a/docs/reference/method/MongoDBGridFSBucket-downloadToStream.txt b/docs/reference/method/MongoDBGridFSBucket-downloadToStream.txt index 3d8e6ebcf..2f2aa5da1 100644 --- a/docs/reference/method/MongoDBGridFSBucket-downloadToStream.txt +++ b/docs/reference/method/MongoDBGridFSBucket-downloadToStream.txt @@ -54,7 +54,9 @@ Examples var_dump(stream_get_contents($destination, -1, 0)); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(6) "foobar" diff --git a/docs/reference/method/MongoDBGridFSBucket-downloadToStreamByName.txt b/docs/reference/method/MongoDBGridFSBucket-downloadToStreamByName.txt index 002bdc654..acc236f57 100644 --- a/docs/reference/method/MongoDBGridFSBucket-downloadToStreamByName.txt +++ b/docs/reference/method/MongoDBGridFSBucket-downloadToStreamByName.txt @@ -20,7 +20,7 @@ Definition .. code-block:: php - function downloadToStreamByName($filename, $destination, array $options = []): void + function downloadToStreamByName(string $filename, resource $destination, array $options = []): void This method has the following parameters: @@ -58,7 +58,9 @@ Examples var_dump(stream_get_contents($destination, -1, 0)); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(6) "foobar" diff --git a/docs/reference/method/MongoDBGridFSBucket-find.txt b/docs/reference/method/MongoDBGridFSBucket-find.txt index c7418cede..e0a4e6621 100644 --- a/docs/reference/method/MongoDBGridFSBucket-find.txt +++ b/docs/reference/method/MongoDBGridFSBucket-find.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function find($filter = [], array $options = []): MongoDB\Driver\Cursor + function find(array|object $filter = [], array $options = []): MongoDB\Driver\Cursor This method has the following parameters: @@ -75,7 +75,9 @@ Examples var_dump($cursor->toArray()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(1) { [0]=> diff --git a/docs/reference/method/MongoDBGridFSBucket-findOne.txt b/docs/reference/method/MongoDBGridFSBucket-findOne.txt index 7b6fcc8fc..f126448d9 100644 --- a/docs/reference/method/MongoDBGridFSBucket-findOne.txt +++ b/docs/reference/method/MongoDBGridFSBucket-findOne.txt @@ -20,7 +20,7 @@ Definition .. code-block:: php - function findOne($filter = [], array $options = []): array|object|null + function findOne(array|object $filter = [], array $options = []): array|object|null This method has the following parameters: @@ -78,7 +78,9 @@ Examples var_dump($fileDocument); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#3004 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBGridFSBucket-getBucketName.txt b/docs/reference/method/MongoDBGridFSBucket-getBucketName.txt index 0452de0a7..52ea92497 100644 --- a/docs/reference/method/MongoDBGridFSBucket-getBucketName.txt +++ b/docs/reference/method/MongoDBGridFSBucket-getBucketName.txt @@ -37,6 +37,8 @@ Examples var_dump($bucket->getBucketName()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(2) "fs" diff --git a/docs/reference/method/MongoDBGridFSBucket-getChunkSizeBytes.txt b/docs/reference/method/MongoDBGridFSBucket-getChunkSizeBytes.txt index f592030a2..16116e562 100644 --- a/docs/reference/method/MongoDBGridFSBucket-getChunkSizeBytes.txt +++ b/docs/reference/method/MongoDBGridFSBucket-getChunkSizeBytes.txt @@ -39,6 +39,8 @@ Examples var_dump($bucket->getChunkSizeBytes()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none int(261120) diff --git a/docs/reference/method/MongoDBGridFSBucket-getChunksCollection.txt b/docs/reference/method/MongoDBGridFSBucket-getChunksCollection.txt index fa5be8322..36dbd8a59 100644 --- a/docs/reference/method/MongoDBGridFSBucket-getChunksCollection.txt +++ b/docs/reference/method/MongoDBGridFSBucket-getChunksCollection.txt @@ -39,6 +39,8 @@ Examples var_dump((string) $bucket->getChunksCollection()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(14) "test.fs.chunks" diff --git a/docs/reference/method/MongoDBGridFSBucket-getDatabaseName.txt b/docs/reference/method/MongoDBGridFSBucket-getDatabaseName.txt index b270b7b57..0b6dd8e0b 100644 --- a/docs/reference/method/MongoDBGridFSBucket-getDatabaseName.txt +++ b/docs/reference/method/MongoDBGridFSBucket-getDatabaseName.txt @@ -37,7 +37,9 @@ Examples var_dump($bucket->getDatabaseName()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(4) "test" diff --git a/docs/reference/method/MongoDBGridFSBucket-getFileDocumentForStream.txt b/docs/reference/method/MongoDBGridFSBucket-getFileDocumentForStream.txt index 052bc79c9..f8d633ed2 100644 --- a/docs/reference/method/MongoDBGridFSBucket-getFileDocumentForStream.txt +++ b/docs/reference/method/MongoDBGridFSBucket-getFileDocumentForStream.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function getFileDocumentForStream($stream): array|object + function getFileDocumentForStream(resource $stream): array|object This method has the following parameters: @@ -54,7 +54,9 @@ Examples fclose($stream); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#4956 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/reference/method/MongoDBGridFSBucket-getFileIdForStream.txt b/docs/reference/method/MongoDBGridFSBucket-getFileIdForStream.txt index 976843b4d..f88dc885a 100644 --- a/docs/reference/method/MongoDBGridFSBucket-getFileIdForStream.txt +++ b/docs/reference/method/MongoDBGridFSBucket-getFileIdForStream.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function getFileIdForStream($stream): mixed + function getFileIdForStream(resource $stream): mixed This method has the following parameters: @@ -55,7 +55,9 @@ Examples fclose($stream); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\BSON\ObjectId)#3005 (1) { ["oid"]=> diff --git a/docs/reference/method/MongoDBGridFSBucket-getFilesCollection.txt b/docs/reference/method/MongoDBGridFSBucket-getFilesCollection.txt index de4196b2f..57813ad6d 100644 --- a/docs/reference/method/MongoDBGridFSBucket-getFilesCollection.txt +++ b/docs/reference/method/MongoDBGridFSBucket-getFilesCollection.txt @@ -41,7 +41,9 @@ Examples var_dump($filesCollection->getCollectionName()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(8) "fs.files" diff --git a/docs/reference/method/MongoDBGridFSBucket-getReadConcern.txt b/docs/reference/method/MongoDBGridFSBucket-getReadConcern.txt index 00517d220..7b12c0513 100644 --- a/docs/reference/method/MongoDBGridFSBucket-getReadConcern.txt +++ b/docs/reference/method/MongoDBGridFSBucket-getReadConcern.txt @@ -37,12 +37,14 @@ Example $database = (new MongoDB\Client)->selectDatabase('test'); $bucket = $database->selectGridFSBucket([ - 'readConcern' => new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::MAJORITY), + 'readConcern' => new MongoDB\Driver\ReadConcern('majority'), ]); var_dump($bucket->getReadConcern()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\ReadConcern)#3 (1) { ["level"]=> diff --git a/docs/reference/method/MongoDBGridFSBucket-getReadPreference.txt b/docs/reference/method/MongoDBGridFSBucket-getReadPreference.txt index 94003eea7..532a6e23e 100644 --- a/docs/reference/method/MongoDBGridFSBucket-getReadPreference.txt +++ b/docs/reference/method/MongoDBGridFSBucket-getReadPreference.txt @@ -43,7 +43,9 @@ Example var_dump($bucket->getReadPreference()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\ReadPreference)#3 (1) { ["mode"]=> diff --git a/docs/reference/method/MongoDBGridFSBucket-getTypeMap.txt b/docs/reference/method/MongoDBGridFSBucket-getTypeMap.txt index cd9c4a1be..7d9a309a3 100644 --- a/docs/reference/method/MongoDBGridFSBucket-getTypeMap.txt +++ b/docs/reference/method/MongoDBGridFSBucket-getTypeMap.txt @@ -46,7 +46,9 @@ Example var_dump($bucket->getTypeMap()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(3) { ["root"]=> diff --git a/docs/reference/method/MongoDBGridFSBucket-getWriteConcern.txt b/docs/reference/method/MongoDBGridFSBucket-getWriteConcern.txt index 1b5577e6c..b77f1f00c 100644 --- a/docs/reference/method/MongoDBGridFSBucket-getWriteConcern.txt +++ b/docs/reference/method/MongoDBGridFSBucket-getWriteConcern.txt @@ -43,7 +43,9 @@ Example var_dump($bucket->getWriteConcern()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Driver\WriteConcern)#3 (2) { ["w"]=> diff --git a/docs/reference/method/MongoDBGridFSBucket-openDownloadStream.txt b/docs/reference/method/MongoDBGridFSBucket-openDownloadStream.txt index 663a69c95..85f2150a3 100644 --- a/docs/reference/method/MongoDBGridFSBucket-openDownloadStream.txt +++ b/docs/reference/method/MongoDBGridFSBucket-openDownloadStream.txt @@ -55,7 +55,9 @@ Examples var_dump(stream_get_contents($downloadStream)); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(6) "foobar" diff --git a/docs/reference/method/MongoDBGridFSBucket-openDownloadStreamByName.txt b/docs/reference/method/MongoDBGridFSBucket-openDownloadStreamByName.txt index d9f7b5b44..cb3a336ee 100644 --- a/docs/reference/method/MongoDBGridFSBucket-openDownloadStreamByName.txt +++ b/docs/reference/method/MongoDBGridFSBucket-openDownloadStreamByName.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function openDownloadStreamByName($filename, array $options = []): resource + function openDownloadStreamByName(string $filename, array $options = []): resource This method has the following parameters: @@ -57,7 +57,9 @@ Examples var_dump(stream_get_contents($bucket->openDownloadStreamByName('filename'))); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(6) "foobar" diff --git a/docs/reference/method/MongoDBGridFSBucket-openUploadStream.txt b/docs/reference/method/MongoDBGridFSBucket-openUploadStream.txt index 806ad6b8b..348b325f4 100644 --- a/docs/reference/method/MongoDBGridFSBucket-openUploadStream.txt +++ b/docs/reference/method/MongoDBGridFSBucket-openUploadStream.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function openUploadStream($filename, array $options = []): resource + function openUploadStream(string $filename, array $options = []): resource This method has the following parameters: @@ -56,7 +56,9 @@ Examples $downloadStream = $bucket->openDownloadStreamByName('filename'); var_dump(stream_get_contents($downloadStream)); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(6) "foobar" diff --git a/docs/reference/method/MongoDBGridFSBucket-rename.txt b/docs/reference/method/MongoDBGridFSBucket-rename.txt index 346ffd81f..888d1ae3f 100644 --- a/docs/reference/method/MongoDBGridFSBucket-rename.txt +++ b/docs/reference/method/MongoDBGridFSBucket-rename.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function rename($id, $newFilename): void + function rename($id, string $newFilename): void This method has the following parameters: @@ -50,6 +50,8 @@ Examples var_dump(stream_get_contents($bucket->openDownloadStreamByName('b'))); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none string(6) "foobar" diff --git a/docs/reference/method/MongoDBGridFSBucket-uploadFromStream.txt b/docs/reference/method/MongoDBGridFSBucket-uploadFromStream.txt index b5d5abefc..18e5944b6 100644 --- a/docs/reference/method/MongoDBGridFSBucket-uploadFromStream.txt +++ b/docs/reference/method/MongoDBGridFSBucket-uploadFromStream.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function uploadFromStream($filename, $source, array $options = []): mixed + function uploadFromStream(string $filename, resource $source, array $options = []): mixed This method has the following parameters: @@ -60,7 +60,9 @@ Examples var_dump($id); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\BSON\ObjectId)#3009 (1) { ["oid"]=> diff --git a/docs/reference/method/MongoDBGridFSBucket__construct.txt b/docs/reference/method/MongoDBGridFSBucket__construct.txt index 49211fba7..c6c309bb3 100644 --- a/docs/reference/method/MongoDBGridFSBucket__construct.txt +++ b/docs/reference/method/MongoDBGridFSBucket__construct.txt @@ -19,7 +19,7 @@ Definition .. code-block:: php - function __construct(MongoDB\Driver\Manager $manager, $databaseName, array $options = []) + function __construct(MongoDB\Driver\Manager $manager, string $databaseName, array $options = []) This constructor has the following parameters: @@ -53,7 +53,9 @@ Examples var_dump($bucket); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\GridFS\Bucket)#3053 (2) { ["bucketName"]=> diff --git a/docs/reference/method/MongoDBMapReduceResult-getCounts.txt b/docs/reference/method/MongoDBMapReduceResult-getCounts.txt index c2feab0ca..35c738ddc 100644 --- a/docs/reference/method/MongoDBMapReduceResult-getCounts.txt +++ b/docs/reference/method/MongoDBMapReduceResult-getCounts.txt @@ -45,7 +45,9 @@ This example reports the count statistics for a map-reduce operation. var_dump($result->getCounts()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(4) { ["input"]=> diff --git a/docs/reference/method/MongoDBMapReduceResult-getExecutionTimeMS.txt b/docs/reference/method/MongoDBMapReduceResult-getExecutionTimeMS.txt index 06c5b012b..05b6bbc17 100644 --- a/docs/reference/method/MongoDBMapReduceResult-getExecutionTimeMS.txt +++ b/docs/reference/method/MongoDBMapReduceResult-getExecutionTimeMS.txt @@ -46,7 +46,9 @@ This example reports the execution time for a map-reduce operation. var_dump($result->getExecutionTimeMS()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none int(244) diff --git a/docs/reference/method/MongoDBMapReduceResult-getIterator.txt b/docs/reference/method/MongoDBMapReduceResult-getIterator.txt index b58040288..cb459cde6 100644 --- a/docs/reference/method/MongoDBMapReduceResult-getIterator.txt +++ b/docs/reference/method/MongoDBMapReduceResult-getIterator.txt @@ -15,7 +15,7 @@ Definition .. phpmethod:: MongoDB\\MapReduceResult::getIterator() - Returns a php:`Traversable `, which may be used to iterate + Returns a :php:`Traversable `, which may be used to iterate through the results of the map-reduce operation. .. code-block:: php @@ -49,7 +49,9 @@ This example iterates through the results of a map-reduce operation. var_dump($population); }; -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(stdClass)#2293 (2) { ["_id"]=> diff --git a/docs/reference/method/MongoDBMapReduceResult-getTiming.txt b/docs/reference/method/MongoDBMapReduceResult-getTiming.txt index 75ab53939..0492b3bec 100644 --- a/docs/reference/method/MongoDBMapReduceResult-getTiming.txt +++ b/docs/reference/method/MongoDBMapReduceResult-getTiming.txt @@ -51,7 +51,9 @@ for a map-reduce operation. var_dump($result->getTiming()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(5) { ["mapTime"]=> diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getCappedMax.txt b/docs/reference/method/MongoDBModelCollectionInfo-getCappedMax.txt index fb86ee19a..dcd8e703f 100644 --- a/docs/reference/method/MongoDBModelCollectionInfo-getCappedMax.txt +++ b/docs/reference/method/MongoDBModelCollectionInfo-getCappedMax.txt @@ -2,6 +2,8 @@ MongoDB\\Model\\CollectionInfo::getCappedMax() ============================================== +.. deprecated:: 1.9 + .. default-domain:: mongodb .. contents:: On this page @@ -28,6 +30,10 @@ Return Values The document limit for the capped collection. If the collection is not capped, ``null`` will be returned. +This method is deprecated in favor of using +:phpmethod:`MongoDB\\Model\\CollectionInfo::getOptions()` and accessing the +``max`` key. + Examples -------- @@ -46,7 +52,9 @@ Examples var_dump($info->getCappedMax()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none int(100) diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getCappedSize.txt b/docs/reference/method/MongoDBModelCollectionInfo-getCappedSize.txt index a3b1ce48f..62732aa47 100644 --- a/docs/reference/method/MongoDBModelCollectionInfo-getCappedSize.txt +++ b/docs/reference/method/MongoDBModelCollectionInfo-getCappedSize.txt @@ -2,6 +2,8 @@ MongoDB\\Model\\CollectionInfo::getCappedSize() =============================================== +.. deprecated:: 1.9 + .. default-domain:: mongodb .. contents:: On this page @@ -29,6 +31,10 @@ Return Values The size limit for the capped collection in bytes. If the collection is not capped, ``null`` will be returned. +This method is deprecated in favor of using +:phpmethod:`MongoDB\\Model\\CollectionInfo::getOptions()` and accessing the +``size`` key. + Examples -------- @@ -46,7 +52,9 @@ Examples var_dump($info->getCappedSize()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none int(1048576) diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getIdIndex.txt b/docs/reference/method/MongoDBModelCollectionInfo-getIdIndex.txt new file mode 100644 index 000000000..4fa22dc00 --- /dev/null +++ b/docs/reference/method/MongoDBModelCollectionInfo-getIdIndex.txt @@ -0,0 +1,75 @@ +============================================ +MongoDB\\Model\\CollectionInfo::getIdIndex() +============================================ + +.. versionadded:: 1.9 + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Definition +---------- + +.. phpmethod:: MongoDB\\Model\\CollectionInfo::getIdIndex() + + Returns information about the ``_id`` field index. + + .. code-block:: php + + function getIdIndex(): array + +Return Values +------------- + +An array containing information on the ``_id`` index. This corresponds to the +``idIndex`` field returned in the ``listCollections`` command reply. + +Examples +-------- + +.. code-block:: php + + 'view', + 'name' => 'foo', + 'idIndex' => [ + 'v' => 2, + 'key' => ['_id' => 1], + 'name' => '_id', + 'ns' => 'test.foo', + ], + ]); + + var_dump($info->getIdIndex()); + +The output would then resemble: + +.. code-block:: none + + array(4) { + ["v"]=> + int(2) + ["key"]=> + array(1) { + ["_id"]=> + int(1) + } + ["name"]=> + string(3) "_id" + ["ns"]=> + string(8) "test.foo" + } + +See Also +-------- + +- :phpmethod:`MongoDB\\Database::createCollection()` +- :manual:`listCollections ` command + reference in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getInfo.txt b/docs/reference/method/MongoDBModelCollectionInfo-getInfo.txt new file mode 100644 index 000000000..4d4f19ff3 --- /dev/null +++ b/docs/reference/method/MongoDBModelCollectionInfo-getInfo.txt @@ -0,0 +1,61 @@ +========================================= +MongoDB\\Model\\CollectionInfo::getInfo() +========================================= + +.. versionadded:: 1.9 + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Definition +---------- + +.. phpmethod:: MongoDB\\Model\\CollectionInfo::getInfo() + + Returns additional information about the collection. + + .. code-block:: php + + function getInfo(): array + +Return Values +------------- + +An array containing extra information about the collection. This corresponds to +the ``info`` field returned in the ``listCollections`` command reply. + +Examples +-------- + +.. code-block:: php + + 'view', + 'name' => 'foo', + 'info' => ['readOnly' => true] + ]); + + var_dump($info->getInfo()); + +The output would then resemble: + +.. code-block:: none + + array(1) { + ["readOnly"]=> + bool(true) + } + +See Also +-------- + +- :phpmethod:`MongoDB\\Database::createCollection()` +- :manual:`listCollections ` command + reference in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getName.txt b/docs/reference/method/MongoDBModelCollectionInfo-getName.txt index 42226c203..d3104af82 100644 --- a/docs/reference/method/MongoDBModelCollectionInfo-getName.txt +++ b/docs/reference/method/MongoDBModelCollectionInfo-getName.txt @@ -24,7 +24,8 @@ Definition Return Values ------------- -The collection name. +The collection name. This corresponds to the ``name`` field returned in the +``listCollections`` command reply. Examples -------- @@ -37,7 +38,9 @@ Examples echo $info->getName(); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none foo diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getOptions.txt b/docs/reference/method/MongoDBModelCollectionInfo-getOptions.txt index 845068163..3ffac1db8 100644 --- a/docs/reference/method/MongoDBModelCollectionInfo-getOptions.txt +++ b/docs/reference/method/MongoDBModelCollectionInfo-getOptions.txt @@ -26,7 +26,8 @@ Definition Return Values ------------- -The collection options. +The collection options. This corresponds to the ``options`` field returned in +the ``listCollections`` command reply. Examples -------- @@ -45,7 +46,9 @@ Examples var_dump($info->getOptions()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(2) { ["capped"]=> diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getType.txt b/docs/reference/method/MongoDBModelCollectionInfo-getType.txt new file mode 100644 index 000000000..9fdf5c990 --- /dev/null +++ b/docs/reference/method/MongoDBModelCollectionInfo-getType.txt @@ -0,0 +1,54 @@ +========================================= +MongoDB\\Model\\CollectionInfo::getType() +========================================= + +.. versionadded:: 1.9 + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Definition +---------- + +.. phpmethod:: MongoDB\\Model\\CollectionInfo::getType() + + Return the collection type. + + .. code-block:: php + + function getType(): string + +Return Values +------------- + +The collection type. This corresponds to the ``type`` field returned in the +``listCollections`` command reply. + +Examples +-------- + +.. code-block:: php + + 'collection', 'name' => 'foo']); + + echo $info->getType(); + +The output would then resemble: + +.. code-block:: none + + collection + +See Also +-------- + +- :phpmethod:`MongoDB\\Database::createCollection()` +- :manual:`listCollections ` command + reference in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelCollectionInfo-isCapped.txt b/docs/reference/method/MongoDBModelCollectionInfo-isCapped.txt index a583a83fc..a6067e0a5 100644 --- a/docs/reference/method/MongoDBModelCollectionInfo-isCapped.txt +++ b/docs/reference/method/MongoDBModelCollectionInfo-isCapped.txt @@ -2,6 +2,8 @@ MongoDB\\Model\\CollectionInfo::isCapped() ========================================== +.. deprecated:: 1.9 + .. default-domain:: mongodb .. contents:: On this page @@ -27,6 +29,10 @@ Return Values A boolean indicating whether the collection is a capped collection. +This method is deprecated in favor of using +:phpmethod:`MongoDB\\Model\\CollectionInfo::getOptions()` and accessing the +``capped`` key. + Examples -------- @@ -44,7 +50,9 @@ Examples var_dump($info->isCapped()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none bool(true) diff --git a/docs/reference/method/MongoDBModelDatabaseInfo-getName.txt b/docs/reference/method/MongoDBModelDatabaseInfo-getName.txt index a2524a4b1..6f82a77ab 100644 --- a/docs/reference/method/MongoDBModelDatabaseInfo-getName.txt +++ b/docs/reference/method/MongoDBModelDatabaseInfo-getName.txt @@ -37,7 +37,9 @@ Examples echo $info->getName(); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none foo diff --git a/docs/reference/method/MongoDBModelDatabaseInfo-getSizeOnDisk.txt b/docs/reference/method/MongoDBModelDatabaseInfo-getSizeOnDisk.txt index d42f23ff5..8c3a0b0b4 100644 --- a/docs/reference/method/MongoDBModelDatabaseInfo-getSizeOnDisk.txt +++ b/docs/reference/method/MongoDBModelDatabaseInfo-getSizeOnDisk.txt @@ -37,7 +37,9 @@ Examples var_dump($info->getSizeOnDisk()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none int(1048576) diff --git a/docs/reference/method/MongoDBModelDatabaseInfo-isEmpty.txt b/docs/reference/method/MongoDBModelDatabaseInfo-isEmpty.txt index ae25e67f2..1c68299ea 100644 --- a/docs/reference/method/MongoDBModelDatabaseInfo-isEmpty.txt +++ b/docs/reference/method/MongoDBModelDatabaseInfo-isEmpty.txt @@ -37,7 +37,9 @@ Examples var_dump($info->isEmpty()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none bool(true) diff --git a/docs/reference/method/MongoDBModelIndexInfo-getKey.txt b/docs/reference/method/MongoDBModelIndexInfo-getKey.txt index 1a115f9cc..ff416f919 100644 --- a/docs/reference/method/MongoDBModelIndexInfo-getKey.txt +++ b/docs/reference/method/MongoDBModelIndexInfo-getKey.txt @@ -41,7 +41,9 @@ Examples var_dump($info->getKey()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none array(1) { ["x"]=> diff --git a/docs/reference/method/MongoDBModelIndexInfo-getName.txt b/docs/reference/method/MongoDBModelIndexInfo-getName.txt index 97ad45fae..b37c66216 100644 --- a/docs/reference/method/MongoDBModelIndexInfo-getName.txt +++ b/docs/reference/method/MongoDBModelIndexInfo-getName.txt @@ -17,8 +17,7 @@ Definition Return the index name. This correlates with the return value of :phpmethod:`MongoDB\\Collection::createIndex()`. An index name may be derived - from the ``$key`` parameter or or explicitly specified via the ``name`` - option. + from the ``$key`` parameter or explicitly specified via the ``name`` option. .. code-block:: php @@ -42,7 +41,9 @@ Examples echo $info->getName(); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none x_1 diff --git a/docs/reference/method/MongoDBModelIndexInfo-getNamespace.txt b/docs/reference/method/MongoDBModelIndexInfo-getNamespace.txt index f4a64bb66..f4bb7a596 100644 --- a/docs/reference/method/MongoDBModelIndexInfo-getNamespace.txt +++ b/docs/reference/method/MongoDBModelIndexInfo-getNamespace.txt @@ -40,7 +40,9 @@ Examples echo $info->getNamespace(); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none foo.bar diff --git a/docs/reference/method/MongoDBModelIndexInfo-getVersion.txt b/docs/reference/method/MongoDBModelIndexInfo-getVersion.txt index 728f38858..96a227706 100644 --- a/docs/reference/method/MongoDBModelIndexInfo-getVersion.txt +++ b/docs/reference/method/MongoDBModelIndexInfo-getVersion.txt @@ -39,7 +39,9 @@ Examples var_dump($info->getVersion()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none int(1) diff --git a/docs/reference/method/MongoDBModelIndexInfo-is2dSphere.txt b/docs/reference/method/MongoDBModelIndexInfo-is2dSphere.txt index a6bb398be..017e6e7bd 100644 --- a/docs/reference/method/MongoDBModelIndexInfo-is2dSphere.txt +++ b/docs/reference/method/MongoDBModelIndexInfo-is2dSphere.txt @@ -46,7 +46,9 @@ Examples } } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none pos_2dsphere has 2dsphereIndexVersion: 3 diff --git a/docs/reference/method/MongoDBModelIndexInfo-isGeoHaystack.txt b/docs/reference/method/MongoDBModelIndexInfo-isGeoHaystack.txt index a2d9a028b..f6c2007df 100644 --- a/docs/reference/method/MongoDBModelIndexInfo-isGeoHaystack.txt +++ b/docs/reference/method/MongoDBModelIndexInfo-isGeoHaystack.txt @@ -4,6 +4,9 @@ MongoDB\\Model\\IndexInfo::isGeoHaystack() .. versionadded:: 1.4 +.. deprecated:: 1.16 + MongoDB 5.0 and later no longer supports geoHaystack indexes. + .. default-domain:: mongodb .. contents:: On this page @@ -17,8 +20,8 @@ Definition .. phpmethod:: MongoDB\\Model\\IndexInfo::isGeoHaystack() - Return whether the index is a :manual:`geoHaystack - ` index. + Return whether the index is a :manual:`geoHaystack ` + index. .. code-block:: php @@ -46,7 +49,9 @@ Examples } } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none pos_geoHaystack_x_1 has bucketSize: 5 diff --git a/docs/reference/method/MongoDBModelIndexInfo-isSparse.txt b/docs/reference/method/MongoDBModelIndexInfo-isSparse.txt index 5bbe4dea4..533793c9e 100644 --- a/docs/reference/method/MongoDBModelIndexInfo-isSparse.txt +++ b/docs/reference/method/MongoDBModelIndexInfo-isSparse.txt @@ -41,7 +41,9 @@ Examples var_dump($info->isSparse()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none bool(true) diff --git a/docs/reference/method/MongoDBModelIndexInfo-isText.txt b/docs/reference/method/MongoDBModelIndexInfo-isText.txt index 083335968..7e0488f5b 100644 --- a/docs/reference/method/MongoDBModelIndexInfo-isText.txt +++ b/docs/reference/method/MongoDBModelIndexInfo-isText.txt @@ -45,7 +45,9 @@ Examples } } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none name_text has default language: english diff --git a/docs/reference/method/MongoDBModelIndexInfo-isTtl.txt b/docs/reference/method/MongoDBModelIndexInfo-isTtl.txt index a3e70d036..a33a22b96 100644 --- a/docs/reference/method/MongoDBModelIndexInfo-isTtl.txt +++ b/docs/reference/method/MongoDBModelIndexInfo-isTtl.txt @@ -41,7 +41,9 @@ Examples var_dump($info->isTtl()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none bool(true) diff --git a/docs/reference/method/MongoDBModelIndexInfo-isUnique.txt b/docs/reference/method/MongoDBModelIndexInfo-isUnique.txt index 30cf1ece5..ef61e7c3a 100644 --- a/docs/reference/method/MongoDBModelIndexInfo-isUnique.txt +++ b/docs/reference/method/MongoDBModelIndexInfo-isUnique.txt @@ -41,7 +41,9 @@ Examples var_dump($info->isUnique()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none bool(true) diff --git a/docs/tutorial.txt b/docs/tutorial.txt index 7735508e6..91333fb45 100644 --- a/docs/tutorial.txt +++ b/docs/tutorial.txt @@ -5,6 +5,8 @@ Tutorials .. toctree:: + /tutorial/connecting + /tutorial/server-selection /tutorial/crud /tutorial/collation /tutorial/commands @@ -15,3 +17,5 @@ Tutorials /tutorial/indexes /tutorial/tailable-cursor /tutorial/example-data + /tutorial/modeling-bson-data + /tutorial/stable-api diff --git a/docs/tutorial/client-side-encryption.txt b/docs/tutorial/client-side-encryption.txt index cc77b07b1..efff2e7d4 100644 --- a/docs/tutorial/client-side-encryption.txt +++ b/docs/tutorial/client-side-encryption.txt @@ -14,6 +14,50 @@ Client-Side Field Level Encryption allows administrators and developers to encrypt specific data fields in addition to other MongoDB encryption features. +Creating an Encryption Key +-------------------------- + +.. note:: + + The following examples use a local master key; however, other key providers + such as AWS KMS are also an option. This master key is used to encrypt data + keys that are stored locally. It is important that you keep this key secure. + +To create an encryption key, create a :php:`MongoDB\\Driver\\ClientEncryption ` +instance with encryption options and create a new data key. The method will +return the key ID which can be used to reference the key later. You can also +pass multiple alternate names for this key and reference the key by these names +instead of the key ID. Creating a new data encryption key would typically be +done on initial deployment, but depending on your use case you may want to use +more than one encryption key or create them dynamically. + +.. code-block:: php + + ', Binary::TYPE_GENERIC); + + $clientEncryptionOpts = [ + 'keyVaultNamespace' => 'encryption.__keyVault', + 'kmsProviders' => [ + 'local' => ['key' => $localKey], + ], + ]; + + $client = new Client(); + $clientEncryption = $client->createClientEncryption($clientEncryptionOpts); + + // Create an encryption key with an alternate name + // To store the key ID for later use, you can use serialize or var_export + $keyId = $clientEncryption->createDataKey('local', ['keyAltNames' => ['my-encryption-key']]); + +.. seealso:: :manual:`Encryption Key Management ` in the MongoDB manual + + Automatic Encryption and Decryption ----------------------------------- @@ -21,9 +65,10 @@ Automatic Encryption and Decryption Auto encryption is an enterprise only feature. -The following example uses a local key, however using AWS Key Management Service -is also an option. The data in the ``encryptedField`` field is automatically -encrypted on insertion and decrypted when querying on the client side. +The following example sets up a collection with automatic encryption based on a +``$jsonSchema`` validator. The data in the ``encryptedField`` field is +automatically encrypted on insertion and decrypted when reading on the client +side. .. code-block:: php @@ -31,24 +76,24 @@ encrypted on insertion and decrypted when querying on the client side. use MongoDB\BSON\Binary; use MongoDB\Client; + use MongoDB\Driver\ClientEncryption; $localKey = new Binary('', Binary::TYPE_GENERIC); - $encryptionOpts = [ - 'keyVaultNamespace' => 'admin.datakeys', + 'keyVaultNamespace' => 'encryption.__keyVault', 'kmsProviders' => [ 'local' => ['key' => $localKey], ], ]; - $client = new Client('mongodb://127.0.0.1'); - $clientEncryption = $client->createClientEncryption($encryptionOpts); + $client = new Client(); $database = $client->selectDatabase('test'); $database->dropCollection('coll'); // remove old data - // Create new key in the key vault and store its ID for later use - $keyId = $clientEncryption->createDataKey('local'); + // This uses the key ID from the first example. The key ID could be read from + // a configuration file. + $keyId = readDataKey(); $database->createCollection('coll', [ 'validator' => [ @@ -79,9 +124,8 @@ encrypted on insertion and decrypted when querying on the client side. Specifying an Explicit Schema for Encryption -------------------------------------------- -The following example shows how to create a new key and store it in the key -vault collection. The encrypted client configures an explicit schema for -encryption using the newly created key. +The following example uses the ``schemaMap`` encryption option to define +encrypted fields. .. note:: @@ -100,21 +144,14 @@ encryption using the newly created key. $localKey = new Binary('', Binary::TYPE_GENERIC); - $clientEncryptionOpts = [ - 'keyVaultNamespace' => 'admin.datakeys', - 'kmsProviders' => [ - 'local' => ['key' => $localKey], - ], - ]; - $client = new Client(); - $clientEncryption = $client->createClientEncryption($clientEncryptionOpts); - // Create new key in the key vault and store its ID for later use - $keyId = $clientEncryption->createDataKey('local'); + // This uses the key ID from the first example. The key ID could be read from + // a configuration file. + $keyId = readDataKey(); $autoEncryptionOpts = [ - 'keyVaultNamespace' => 'admin.datakeys', + 'keyVaultNamespace' => 'encryption.__keyVault', 'kmsProviders' => [ 'local' => ['key' => $localKey], ], @@ -134,7 +171,7 @@ encryption using the newly created key. ], ]; - $encryptedClient = new Client('mongodb://127.0.0.1', [], ['autoEncryption' => $autoEncryptionOpts]); + $encryptedClient = new Client(null, [], ['autoEncryption' => $autoEncryptionOpts]); $collection = $encryptedClient->selectCollection('test', 'coll'); $collection->drop(); // clear old data @@ -147,10 +184,10 @@ encryption using the newly created key. Manually Encrypting and Decrypting Values ----------------------------------------- -In the MongoDB Community Edition, you will have to manually encrypt and decrypt -values before storing them in the database. The following example assumes that -you have already created an encryption key in the key vault collection and -explicitly encrypts and decrypts values in the document. +In the MongoDB Community Edition, you will have to manually encrypt values +before storing them in the database. The following example assumes that you have +already created an encryption key in the key vault collection and explicitly +encrypts and decrypts values in the document. .. code-block:: php @@ -163,7 +200,7 @@ explicitly encrypts and decrypts values in the document. $localKey = new Binary('', Binary::TYPE_GENERIC); $clientEncryptionOpts = [ - 'keyVaultNamespace' => 'admin.datakeys', + 'keyVaultNamespace' => 'encryption.__keyVault', 'kmsProviders' => [ 'local' => ['key' => $localKey], ], @@ -172,8 +209,9 @@ explicitly encrypts and decrypts values in the document. $client = new Client(); $clientEncryption = $client->createClientEncryption($clientEncryptionOpts); - // Create new key in the key vault and store its ID for later use - $keyId = $clientEncryption->createDataKey('local'); + // This uses the key ID from the first example. The key ID could be read from + // a configuration file. + $keyId = readDataKey(); $collection = $client->selectCollection('test', 'coll'); $collection->drop(); // clear old data @@ -195,12 +233,15 @@ Referencing Encryption Keys by an Alternative Name While it is possible to create an encryption key every time data is encrypted, this is not the recommended approach. Instead, you should create your encryption -keys depending on your use-case, e.g. by creating a user-specific encryption +keys depending on your use case, e.g. by creating a user-specific encryption key. To reference keys in your software, you can use the keyAltName attribute specified when creating the key. The following example creates an encryption key with an alternative name, which could be done when deploying the application. The software then encrypts data by referencing the key by its alternative name. +To use an alternate name when referencing an encryption key, use the +``keyAltName`` option instead of ``keyId``. + .. code-block:: php ', Binary::TYPE_GENERIC); $clientEncryptionOpts = [ - 'keyVaultNamespace' => 'admin.datakeys', + 'keyVaultNamespace' => 'encryption.__keyVault', 'kmsProviders' => [ 'local' => ['key' => $localKey], ], @@ -221,16 +262,13 @@ The software then encrypts data by referencing the key by its alternative name. $client = new Client(); $clientEncryption = $client->createClientEncryption($clientEncryptionOpts); - // Create an encryption key with an alternative name. This could be done when - // deploying the application - $keyId = $clientEncryption->createDataKey('local', ['keyAltNames' => ['altname']]); - $collection = $client->selectCollection('test', 'coll'); $collection->drop(); // clear old data - // Reference the encryption key we created earlier by its alternative name + // Reference the encryption key created in the first example by its + // alternative name $encryptionOpts = [ - 'keyAltName' => 'altname', + 'keyAltName' => 'my-encryption-key', 'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, ]; $encryptedValue = $clientEncryption->encrypt('123456789', $encryptionOpts); @@ -239,3 +277,92 @@ The software then encrypts data by referencing the key by its alternative name. $document = $collection->findOne(); var_dump($clientEncryption->decrypt($document->encryptedField)); + + +Automatic Queryable Encryption +------------------------------ + +.. note:: + + Automatic queryable encryption is an enterprise only feature and requires + MongoDB 7.0+. + +The following example uses a local key; however, other key providers such as AWS +are also an option. The data in the ``encryptedIndexed`` and +``encryptedUnindexed`` fields will be automatically encrypted on insertion and +decrypted when querying on the client side. Additionally, it is possible to +query on the ``encryptedIndexed`` field. + +.. code-block:: php + + ', Binary::TYPE_GENERIC); + + $encryptionOpts = [ + 'keyVaultNamespace' => 'encryption.__keyVault', + 'kmsProviders' => ['local' => ['key' => $localKey]], + ]; + + $client = new Client(); + $clientEncryption = $client->createClientEncryption($encryptionOpts); + + // Create two data keys, one for each encrypted field + $dataKeyId1 = $clientEncryption->createDataKey('local'); + $dataKeyId2 = $clientEncryption->createDataKey('local'); + + $autoEncryptionOpts = [ + 'keyVaultNamespace' => 'encryption.__keyVault', + 'kmsProviders' => ['local' => ['key' => $localKey]], + 'encryptedFieldsMap' => [ + 'test.coll' => [ + 'fields' => [ + [ + 'path' => 'encryptedIndexed', + 'bsonType' => 'string', + 'keyId' => $dataKeyId1, + 'queries' => ['queryType' => 'equality'], + ], + [ + 'path' => 'encryptedUnindexed', + 'bsonType' => 'string', + 'keyId' => $dataKeyId2, + ], + ], + ], + ], + ]; + + $encryptedClient = new Client(null, [], ['autoEncryption' => $autoEncryptionOpts]); + + /* Drop and create the collection under test. The createCollection() helper + * will reference the client's encryptedFieldsMap and create additional, + * internal collections automatically. */ + $encryptedClient->selectDatabase('test')->dropCollection('coll'); + $encryptedClient->selectDatabase('test')->createCollection('coll'); + $encryptedCollection = $encryptedClient->selectCollection('test', 'coll'); + + /* Using a client with auto encryption, insert a document with encrypted + * fields and assert that those fields are automatically decrypted when + * querying. The encryptedIndexed and encryptedUnindexed fields should both + * be strings. */ + $indexedValue = 'indexedValue'; + $unindexedValue = 'unindexedValue'; + + $encryptedCollection->insertOne([ + '_id' => 1, + 'encryptedIndexed' => $indexedValue, + 'encryptedUnindexed' => $unindexedValue, + ]); + + var_dump($encryptedCollection->findOne(['encryptedIndexed' => $indexedValue])); + + /* Using a client without auto encryption, query for the same document and + * assert that encrypted data is returned. The encryptedIndexed and + * encryptedUnindexed fields should both be Binary objects. */ + $unencryptedCollection = $client->selectCollection('test', 'coll'); + + var_dump($unencryptedCollection->findOne(['_id' => 1])); diff --git a/docs/tutorial/collation.txt b/docs/tutorial/collation.txt index 0b1bfabd9..28225db01 100644 --- a/docs/tutorial/collation.txt +++ b/docs/tutorial/collation.txt @@ -66,7 +66,7 @@ Collation Parameters ] The only required parameter is ``locale``, which the server parses as an `ICU -format locale ID `_. For example, set +format locale ID `_. For example, set ``locale`` to ``en_US`` to represent US English or ``fr_CA`` to represent Canadian French. @@ -196,7 +196,7 @@ A collection called ``names`` contains the following documents: { "_id" : 4, "first_name" : "Jürgen" } The following :phpmethod:`findOneAndUpdate() -` operation on the collection does not +` operation on the collection does not specify a collation. .. code-block:: php @@ -206,7 +206,7 @@ specify a collation. $collection = (new MongoDB\Client)->test->names; $document = $collection->findOneAndUpdate( - ['first_name' => ['$lt' =-> 'Gunter']], + ['first_name' => ['$lt' => 'Gunter']], ['$set' => ['verified' => true]] ); @@ -214,7 +214,7 @@ Because ``Gunter`` is lexically first in the collection, the above operation returns no results and updates no documents. Consider the same :phpmethod:`findOneAndUpdate() -` operation but with a collation +` operation but with a collation specified, which uses the locale ``de@collation=phonebook``. .. note:: @@ -231,7 +231,7 @@ specified, which uses the locale ``de@collation=phonebook``. $collection = (new MongoDB\Client)->test->names; $document = $collection->findOneAndUpdate( - ['first_name' => ['$lt' =-> 'Gunter']], + ['first_name' => ['$lt' => 'Gunter']], ['$set' => ['verified' => true]], [ 'collation' => ['locale' => 'de@collation=phonebook'], @@ -349,7 +349,7 @@ Aggregation ~~~~~~~~~~~ To use collation with an :phpmethod:`aggregate() -` operation, specify a collation in the +` operation, specify a collation in the aggregation options. The following aggregation example uses a collection called ``names`` and groups diff --git a/docs/tutorial/commands.txt b/docs/tutorial/commands.txt index 88ce5ec9a..bccf74626 100644 --- a/docs/tutorial/commands.txt +++ b/docs/tutorial/commands.txt @@ -20,17 +20,18 @@ The |php-library| provides helper methods across the :phpclass:`Client :phpmethod:`MongoDB\\Database::command()` method may be used to run database commands that do not have a helper method. -Execute Database Commands -------------------------- +The :phpmethod:`MongoDB\\Database::command()` method always returns a +:php:`MongoDB\\Driver\\Cursor ` object, since it must +support execution of commands that return single result documents *and* multiple +results via a command cursor. -Basic Command -~~~~~~~~~~~~~ +Commands That Return a Single Result Document +--------------------------------------------- -To execute a command on a :program:`mongod` instance, use the -:phpmethod:`MongoDB\\Database::command()` method. For instance, the following -operation uses the :manual:`geoNear ` command to -search for the three closest documents to longitude ``-74`` and latitude ``40`` -in the ``restos`` collection in the ``test`` database: +Most database commands return a single result document, which can be obtained by +converting the returned cursor to an array and accessing its first element. The +following example executes a :manual:`ping ` command +and prints its result document: .. code-block:: php @@ -38,194 +39,90 @@ in the ``restos`` collection in the ``test`` database: $database = (new MongoDB\Client)->test; - $cursor = $database->command([ - 'geoNear' => 'restos', - 'near' => [ - 'type' => 'Point', - 'coordinates' => [-74.0, 40.0], - ], - 'spherical' => 'true', - 'num' => 3, - ]); + $cursor = $database->command(['ping' => 1]); - $results = $cursor->toArray()[0]; + var_dump($cursor->toArray()[0]); - var_dump($results); +The output would resemble: -The output would then resemble:: +.. code-block:: none - object(MongoDB\Model\BSONDocument)#27 (1) { + object(MongoDB\Model\BSONDocument)#11 (1) { ["storage":"ArrayObject":private]=> - array(4) { - ["waitedMS"]=> - int(0) - ["results"]=> - object(MongoDB\Model\BSONArray)#25 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - [0]=> - object(MongoDB\Model\BSONDocument)#14 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["dis"]=> - float(39463.618389163) - ["obj"]=> - object(MongoDB\Model\BSONDocument)#13 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#3 (1) { - ["oid"]=> - string(24) "55cba2486c522cafdb059bed" - } - ["location"]=> - object(MongoDB\Model\BSONDocument)#12 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["coordinates"]=> - object(MongoDB\Model\BSONArray)#11 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - [0]=> - float(-74.1641319) - [1]=> - float(39.6686512) - } - } - ["type"]=> - string(5) "Point" - } - } - ["name"]=> - string(32) "Soul Food Kitchen Seafood Heaven" - } - } - } - } - [1]=> - object(MongoDB\Model\BSONDocument)#19 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["dis"]=> - float(50686.851650416) - ["obj"]=> - object(MongoDB\Model\BSONDocument)#18 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#15 (1) { - ["oid"]=> - string(24) "55cba2476c522cafdb0544df" - } - ["location"]=> - object(MongoDB\Model\BSONDocument)#17 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["coordinates"]=> - object(MongoDB\Model\BSONArray)#16 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - [0]=> - float(-74.2566332) - [1]=> - float(40.4109872) - } - } - ["type"]=> - string(5) "Point" - } - } - ["name"]=> - string(20) "Seguine Bagel Bakery" - } - } - } - } - [2]=> - object(MongoDB\Model\BSONDocument)#24 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["dis"]=> - float(58398.379630263) - ["obj"]=> - object(MongoDB\Model\BSONDocument)#23 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#20 (1) { - ["oid"]=> - string(24) "55cba2476c522cafdb053c92" - } - ["location"]=> - object(MongoDB\Model\BSONDocument)#22 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["coordinates"]=> - object(MongoDB\Model\BSONArray)#21 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - [0]=> - float(-74.3731727) - [1]=> - float(40.4404759) - } - } - ["type"]=> - string(5) "Point" - } - } - ["name"]=> - string(17) "Water'S Edge Club" - } - } - } - } - } - } - ["stats"]=> - object(MongoDB\Model\BSONDocument)#26 (1) { - ["storage":"ArrayObject":private]=> - array(5) { - ["nscanned"]=> - int(25139) - ["objectsLoaded"]=> - int(25134) - ["avgDistance"]=> - float(49516.283223281) - ["maxDistance"]=> - float(58398.379630263) - ["time"]=> - int(126) - } - } + array(1) { ["ok"]=> float(1) } } -Commands with Custom Read Preference -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Commands That Yield Multiple Results +------------------------------------ + +Some database commands return a cursor with multiple results. The following +example executes :manual:`listCollections `, +which returns a cursor containing a result document for each collection in the +``test`` database, and iterates through the results using a ``foreach`` loop. +Note that this example is illustrative; applications would generally use +:phpmethod:`MongoDB\\Database::listCollections()` in practice. + +.. code-block:: php + + test; + + $cursor = $database->command(['listCollections' => 1]); + + foreach ($cursor as $collection) { + echo $collection['name'], "\n"; + } + +The output might resemble the following: -Some commands, such as :manual:`createUser `, may -only be executed on a :term:`primary` replica set member or a -:term:`standalone`. +.. code-block:: none -The command helper methods in the |php-library|, such as + persons + posts + zips + +.. note:: + + At the *protocol* level, commands that yield multiple results via a cursor + will return a single result document with the essential ingredients for + constructing the cursor (i.e. the cursor's ID, namespace, and an optional + first batch of results). If the :php:`MongoDB\Driver\Manager::executeCommand() + ` method in the PHP driver detects + such a response, it will construct an iterable command cursor and return it + instead of the raw result document. If necessary, raw result documents can + still be observed using `command monitoring + `_. + +Specifying a Custom Read Preference +----------------------------------- + +Write commands, such as :manual:`createUser `, +can only be executed on a writable server (e.g. :term:`primary` replica set +member). Command helper methods in the |php-library|, such as :phpmethod:`MongoDB\\Database::drop()`, know to apply their own :term:`read preference` if necessary. However, the :phpmethod:`MongoDB\\Database::command()` method is a generic method and defaults to the read preference of the Database -object on which it is invoked. To execute commands that require a specific read -preference, specify the read preference in the ``$options`` parameter of the -method. +object on which it is invoked. When necessary, the ``readPreference`` option may +be used to override the default read preference. -The following example adds a user to the ``test`` database and specifies a -custom read preference: +The following example connects to a cluster and specifies ``secondaryPreferred`` +as the Client's default read preference. It then specifies a ``primary`` read +preference when executing the ``createUser`` command on the ``test`` database: .. code-block:: php test; + $client = new MongoDB\Client( + 'mongodb+srv://cluster0.example.com', + ['readPreference' => 'secondaryPreferred'] + ); + + $client->test; $cursor = $db->command( [ @@ -234,92 +131,20 @@ custom read preference: 'roles' => ['readWrite'], ], [ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_PRIMARY), + 'readPreference' => new MongoDB\Driver\ReadPreference('primary'), ] ); var_dump($cursor->toArray()[0]); -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#8 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["ok"]=> - float(1) - } - } - -View Command Results --------------------- - -View Single Result Documents -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The output would then resemble: -The :phpmethod:`MongoDB\\Database::command()` method returns a -:php:`MongoDB\\Driver\\Cursor ` object. +.. code-block:: none -Many MongoDB commands return their responses as a single document. To read the -command response, you may either iterate on the cursor and access the first -document, or access the first result in the array, as in the following: - -.. code-block:: php - - test; - - $cursor = $database->command(['ping' => 1]); - - var_dump($cursor->toArray()[0]); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#2 (1) { + object(MongoDB\Model\BSONDocument)#8 (1) { ["storage":"ArrayObject":private]=> array(1) { ["ok"]=> float(1) } } - -Iterate Results from a Cursor -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Some commands, such as :manual:`listCollections -`, return their results via an iterable -cursor. To view the results, iterate through the cursor. - -The following example lists the collections in the ``test`` database by -iterating through the cursor returned by the ``listCollections`` command using a -``foreach`` loop: - -.. code-block:: php - - test; - - $cursor = $database->command(['listCollections' => 1]); - - foreach ($cursor as $collection) { - echo $collection['name'], "\n"; - } - -The output would then be a list of the values for the ``name`` key, for -instance:: - - persons - posts - zips - -.. note:: - - At the *protocol* level, commands that support a cursor return a single - result document with the essential ingredients for constructing the command - cursor (i.e. the cursor's ID, namespace, and the first batch of results). In - the PHP driver implementation, the - :php:`MongoDB\Driver\Manager::executeCommand() - ` method detects such a result and - constructs the iterable command cursor, which is returned rather than the - base result document. diff --git a/docs/tutorial/connecting.txt b/docs/tutorial/connecting.txt new file mode 100644 index 000000000..c964362a0 --- /dev/null +++ b/docs/tutorial/connecting.txt @@ -0,0 +1,25 @@ +===================== +Connecting to MongoDB +===================== + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Creating a Client instance +-------------------------------------------------------- + +.. include:: /reference/method/MongoDBClient__construct.txt + :start-after: start-connecting-include + :end-before: end-connecting-include + +Specifying connection options +----------------------------- + +Connection options can be passed via the ``$uri`` parameter, or through the +``$options`` and ``$driverOptions`` parameters. The available options are +documented in the :phpmethod:`MongoDB\\Client::__construct()` reference. diff --git a/docs/tutorial/crud.txt b/docs/tutorial/crud.txt index 1e9f95e2e..b5588acb1 100644 --- a/docs/tutorial/crud.txt +++ b/docs/tutorial/crud.txt @@ -61,7 +61,9 @@ The following example inserts a document while specifying the value for the var_dump($insertOneResult->getInsertedId()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Inserted 1 document(s) int(1) @@ -113,7 +115,9 @@ The following example searches for the document with ``_id`` of ``"94301"``: var_dump($document); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#13 (1) { ["storage":"ArrayObject":private]=> @@ -175,7 +179,9 @@ specified city and state values: echo $document['_id'], "\n"; } -The output would resemble:: +The output would resemble: + +.. code-block:: none 07302 07304 @@ -231,7 +237,9 @@ returned. It also limits the results to 5 documents. var_dump($restaurant); }; -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#10 (1) { ["storage":"ArrayObject":private]=> @@ -325,7 +333,9 @@ five most populous zip codes in the United States: printf("%s: %s, %s\n", $document['_id'], $document['city'], $document['state']); } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none 60623: CHICAGO, IL 11226: BROOKLYN, NY @@ -358,7 +368,9 @@ name starts with "garden" and the state is Texas: printf("%s: %s, %s\n", $document['_id'], $document['city'], $document['state']); } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none 78266: GARDEN RIDGE, TX 79739: GARDEN CITY, TX @@ -368,6 +380,8 @@ An equivalent filter could be constructed using the :query:`$regex` operator: .. code-block:: php + ['$regex' => '^garden', '$options' => 'i'], 'state' => 'TX', @@ -422,7 +436,9 @@ with them: printf("%s has %d zip codes\n", $state['_id'], $state['count']); } -The output would then resemble:: +The output would then resemble: + +.. code-block:: none TX has 1671 zip codes NY has 1595 zip codes @@ -472,7 +488,9 @@ is ``"ny"`` to include a ``country`` field set to ``"us"``: Since the update operation uses the :phpmethod:`MongoDB\\Collection::updateOne()` method, which updates the first -document to match the filter criteria, the results would then resemble:: +document to match the filter criteria, the results would then resemble: + +.. code-block:: none Matched 1 document(s) Modified 1 document(s) @@ -498,7 +516,9 @@ value, as in this example: printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); The number of matched documents and the number of *modified* documents would -therefore not be equal, and the output from the operation would resemble:: +therefore not be equal, and the output from the operation would resemble: + +.. code-block:: none Matched 1 document(s) Modified 0 document(s) @@ -521,7 +541,7 @@ updates to perform. The :phpmethod:`MongoDB\\Collection::updateMany()` reference describes each parameter in detail. The following example inserts three documents into an empty ``users`` collection -in the ``test`` database and then uses the :query:`$set` operator to update the +in the ``test`` database and then uses the :update:`$set` operator to update the documents matching the filter criteria to include the ``country`` field with value ``"us"``: @@ -547,7 +567,9 @@ If an update operation results in no change to a document, such as setting the value of the field to its current value, the number of modified documents can be less than the number of *matched* documents. Since the update document with ``name`` of ``"Bob"`` results in no changes to the document, the output of the -operation therefore resembles:: +operation therefore resembles: + +.. code-block:: none Matched 3 document(s) Modified 2 document(s) @@ -573,7 +595,7 @@ replacement document that will replace the original document in MongoDB. The :phpmethod:`MongoDB\\Collection::replaceOne()` reference describes each parameter in detail. -.. important:: +.. important:: Replacement operations replace all of the fields in a document except the ``_id`` value. To avoid accidentally overwriting or deleting desired fields, @@ -600,12 +622,14 @@ the ``test`` database, and then replaces that document with a new one: printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Matched 1 document(s) Modified 1 document(s) -.. seealso:: +.. seealso:: - :phpmethod:`MongoDB\\Collection::replaceOne()` - :phpmethod:`MongoDB\\Collection::findOneAndReplace()` @@ -649,7 +673,9 @@ the ``upsert`` option set to ``true`` and an empty ``users`` collection in the var_dump($upsertedDocument); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Matched 0 document(s) Modified 0 document(s) @@ -705,7 +731,9 @@ value is ``"ny"``: printf("Deleted %d document(s)\n", $deleteResult->getDeletedCount()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Deleted 1 document(s) @@ -739,7 +767,9 @@ value is ``"ny"``: printf("Deleted %d document(s)\n", $deleteResult->getDeletedCount()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Deleted 2 document(s) diff --git a/docs/tutorial/custom-types.txt b/docs/tutorial/custom-types.txt index 574604302..5fa5880f3 100644 --- a/docs/tutorial/custom-types.txt +++ b/docs/tutorial/custom-types.txt @@ -14,7 +14,7 @@ The driver serializes PHP variables, including objects, into BSON when it communicates to the server, and deserializes BSON back into PHP variables when it receives data from the server. -It is possible to influence the behaviour by implementing the +It is possible to influence the behavior by implementing the :php:`MongoDB\\BSON\\Persistable ` interface. If a class implements this interface, then upon serialization the :php:`bsonSerialize ` method is @@ -152,7 +152,9 @@ back to ``LocalDateTime``. var_dump($document->date->toDateTime()); ?> -Which outputs:: +Which outputs: + +.. code-block:: none object(stdClass)#1 (1) { ["date"]=> diff --git a/docs/tutorial/decimal128.txt b/docs/tutorial/decimal128.txt index a94d0ea5e..3bdab8f13 100644 --- a/docs/tutorial/decimal128.txt +++ b/docs/tutorial/decimal128.txt @@ -48,7 +48,9 @@ field of a collection named ``inventory``: var_dump($item); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#9 (1) { ["storage":"ArrayObject":private]=> @@ -86,7 +88,9 @@ The following example adds two ``Decimal128`` values and creates a new var_dump($sum); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\BSON\Decimal128)#4 (1) { ["dec"]=> @@ -111,7 +115,9 @@ obtain the expected result: var_dump($sum); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none object(MongoDB\BSON\Decimal128)#4 (1) { ["dec"]=> diff --git a/docs/tutorial/example-data.txt b/docs/tutorial/example-data.txt index cf3230952..920de5877 100644 --- a/docs/tutorial/example-data.txt +++ b/docs/tutorial/example-data.txt @@ -9,7 +9,7 @@ Some examples in this documentation use example data fixtures from `primer-dataset.json `_. Importing the dataset into MongoDB can be done in several ways. The following -example imports the `zips.json` file into a `test.zips` collection: +example imports the ``zips.json`` file into a ``test.zips`` collection: :php:`driver ` directly: .. code-block:: php @@ -32,7 +32,9 @@ example imports the `zips.json` file into a `test.zips` collection: $result = $manager->executeBulkWrite('test.zips', $bulk); printf("Inserted %d documents\n", $result->getInsertedCount()); -The output would then resemble:: +The output would then resemble: + +.. code-block:: none Inserted 29353 documents diff --git a/docs/tutorial/gridfs.txt b/docs/tutorial/gridfs.txt index ba9b83681..4eb7e1566 100644 --- a/docs/tutorial/gridfs.txt +++ b/docs/tutorial/gridfs.txt @@ -23,19 +23,19 @@ Creating a GridFS Bucket You can construct a GridFS bucket using the PHP extension's :php:`MongoDB\\Driver\\Manager ` class, or select a bucket from the |php-library|'s :phpclass:`MongoDB\\Database` class via the -:phpmethod:`selectGridFSBucket() ` +:phpmethod:`selectGridFSBucket() ` method. The bucket can be constructed with various options: - - ``bucketName`` determines the prefix for the bucket's metadata and chunk - collections. The default value is ``"fs"``. - - ``chunkSizeBytes`` determines the size of each chunk. GridFS divides the - file into chunks of this length, except for the last, which is only as large - as needed. The default size is ``261120`` (i.e. 255 KiB). - - ``readConcern``, ``readPreference`` and ``writeConcern`` options can be used - to specify defaults for read and write operations, much like the - :phpclass:`MongoDB\\GridFS\\Collection` options. +- ``bucketName`` determines the prefix for the bucket's metadata and chunk + collections. The default value is ``"fs"``. +- ``chunkSizeBytes`` determines the size of each chunk. GridFS divides the file + into chunks of this length, except for the last, which is only as large as + needed. The default size is ``261120`` (i.e. 255 KiB). +- ``readConcern``, ``readPreference`` and ``writeConcern`` options can be used + to specify defaults for read and write operations, much like the + :phpclass:`MongoDB\\GridFS\\Collection` options. Uploading Files with Writable Streams ------------------------------------- @@ -112,9 +112,9 @@ number. Revision numbers are used to distinguish between files sharing the same ``filename`` metadata field, ordered by date of upload (i.e. the ``uploadDate`` metadata field). The ``revision`` option accepted by :phpmethod:`openDownloadStreamByName() -` and +` and :phpmethod:`downloadToStreamByName() -` can be positive or negative. +` can be positive or negative. A positive ``revision`` number may be used to select files in order of the oldest upload date. A revision of ``0`` would denote the file with the oldest @@ -157,7 +157,7 @@ You can delete a GridFS file by its ``_id``. Finding File Metadata --------------------- -The :phpmethod:`find() ` method allows you to +The :phpmethod:`find() ` method allows you to retrieve documents from the GridFS files collection, which contain metadata about the GridFS files. @@ -173,7 +173,7 @@ Accessing File Metadata for an Existing Stream ---------------------------------------------- The :phpmethod:`getFileDocumentForStream() -` method may be used to get +` method may be used to get the file document for an existing readable or writable GridFS stream. .. code-block:: php @@ -193,16 +193,16 @@ the file document for an existing readable or writable GridFS stream. Since the file document for a writable stream is not committed to MongoDB until the stream is closed, :phpmethod:`getFileDocumentForStream() - ` can only return an + ` can only return an in-memory document, which will be missing some fields (e.g. ``length``, ``md5``). The :phpmethod:`getFileIdForStream() -` method may be used to get the +` method may be used to get the ``_id`` for an existing readable or writable GridFS stream. This is a convenience for accessing the ``_id`` property of the object returned by :phpmethod:`getFileDocumentForStream() -`. +`. .. code-block:: php diff --git a/docs/tutorial/indexes.txt b/docs/tutorial/indexes.txt index 0ffe7bdb9..e04d43653 100644 --- a/docs/tutorial/indexes.txt +++ b/docs/tutorial/indexes.txt @@ -31,7 +31,7 @@ Create indexes with the :phpmethod:`MongoDB\\Collection::createIndex()` or reference for more details about each method. The following example creates an ascending index on the ``state`` field using -the :phpmethod:`createIndex() ` method: +the :phpmethod:`createIndex() ` method: .. code-block:: php @@ -45,7 +45,9 @@ the :phpmethod:`createIndex() ` method: When you create an index, the method returns its name, which is automatically generated from its specification. The above example would output something -similar to:: +similar to: + +.. code-block:: none string(7) "state_1" @@ -71,7 +73,9 @@ The following example lists all indexes in the ``zips`` collection in the var_dump($indexInfo); } -The output would resemble:: +The output would resemble: + +.. code-block:: none object(MongoDB\Model\IndexInfo)#10 (4) { ["v"]=> @@ -120,7 +124,9 @@ The following example drops a single index by its name, ``state_1``: var_dump($result); -The operation's output would resemble:: +The operation's output would resemble: + +.. code-block:: none object(MongoDB\Model\BSONDocument)#11 (1) { ["storage":"ArrayObject":private]=> diff --git a/docs/tutorial/install-php-library.txt b/docs/tutorial/install-php-library.txt index acaa8ed10..af409c360 100644 --- a/docs/tutorial/install-php-library.txt +++ b/docs/tutorial/install-php-library.txt @@ -41,33 +41,25 @@ file: extension=mongodb.so -Windows users can download precompiled binaries of the extension from -`PECL `_. After extracting the -``php_mongodb.dll`` file to PHP's extension directory, add the following line to -your ``php.ini`` file: +Windows users can download precompiled binaries of the extension from its +`GitHub releases `__. +After downloading the appropriate archive for your PHP environment, extract the +``php_mongodb.dll`` file to PHP's extension directory and add the following line +to your ``php.ini`` file: .. code-block:: ini extension=php_mongodb.dll -Windows binaries are available for various combinations of PHP version, -thread-safety, and architecture. Failure to select the correct binary will -result in an error attempting to load the extension DLL at runtime. Additional -considerations for Windows are discussed in the -:php:`Windows installation documentation `. - -.. note:: - - If your system has multiple versions of PHP installed, each version will have - its own ``pecl`` command and ``php.ini`` file. Additionally, PHP may also use - separate ``php.ini`` files for its web and CLI environments. If the extension - has been installed but is not available at runtime, double-check that you - have used the correct ``pecl`` command (or binary in the case of Windows) and - have modified the appropriate ``php.ini`` file(s). +See :php:`Installing the MongoDB PHP Driver on Windows ` +for additional information. Installing the Library ---------------------- +Using Composer +~~~~~~~~~~~~~~ + The preferred method of installing the |php-library| is with `Composer `_ by running the following command from your project root: @@ -76,13 +68,6 @@ your project root: composer require mongodb/mongodb -While not recommended, you may also manually install the library using a source -archive attached to the -`GitHub releases `_. - -Configure Autoloading -~~~~~~~~~~~~~~~~~~~~~ - Once you have installed the library, ensure that your application includes Composer's autoloader as in the following example: @@ -96,11 +81,20 @@ Refer to Composer's `autoloading documentation `_ for more information about setting up autoloading. -If you installed the library manually from a source archive, you will need to -manually configure autoloading: - -#. Map the top-level ``MongoDB\`` namespace to the ``src/`` directory - using your preferred autoloader implementation. +Manual Installation Without Composer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -#. Manually require the ``src/functions.php`` file. This is necessary because - PHP does not support autoloading for functions. +While not recommended, you may also manually install the library using a source +archive attached to the +`GitHub releases `__. +When installing the library without Composer, you must ensure that all library +classes *and* functions are loaded for your application: + +#. If you are using a `PSR-4 `_ autoloader, + map the top-level ``MongoDB\`` namespace to the ``src/`` directory. If you + are not using an autoloader, manually require _all_ class files found + recursively within the ``src/`` directory. + +#. Regardless of whether you are using an autoloader, manually require the + ``src/functions.php`` file. This is necessary because PHP does not support + autoloading for functions. diff --git a/docs/tutorial/modeling-bson-data.txt b/docs/tutorial/modeling-bson-data.txt new file mode 100644 index 000000000..24486d1e1 --- /dev/null +++ b/docs/tutorial/modeling-bson-data.txt @@ -0,0 +1,215 @@ +================== +Modeling BSON Data +================== + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + + +Type Maps +--------- + +Most methods that read data from MongoDB support a ``typeMap`` option, which +allows control over how BSON is converted to PHP. Additionally, +the :phpclass:`MongoDB\\Client`, :phpclass:`MongoDB\\Database`, and +:phpclass:`MongoDB\\Collection` classes accept a ``typeMap`` option, which can +be used to specify a default type map to apply to any supporting methods and +selected classes (e.g. :phpmethod:`MongoDB\\Client::selectDatabase()`). + +The :phpclass:`MongoDB\\Client`, :phpclass:`MongoDB\\Database`, and +:phpclass:`MongoDB\\Collection` classes use the following type map by +default: + +.. code-block:: php + + [ + 'array' => 'MongoDB\Model\BSONArray', + 'document' => 'MongoDB\Model\BSONDocument', + 'root' => 'MongoDB\Model\BSONDocument', + ] + +The type map above will convert BSON documents and arrays to +:phpclass:`MongoDB\\Model\\BSONDocument` and +:phpclass:`MongoDB\\Model\\BSONArray` objects, respectively. The ``root`` and +``document`` keys are used to distinguish the top-level BSON document from +embedded documents, respectively. + +A type map may specify any class that implements +:php:`MongoDB\\BSON\\Unserializable ` as well as +``"array"``, ``"stdClass``", and ``"object"`` (``"stdClass``" and ``"object"`` +are aliases of one another). + +.. seealso:: :php:`Deserialization from BSON ` in the PHP manual + + +Persistable Classes +------------------- + +The driver's :php:`persistence specification ` outlines how +classes implementing its :php:`MongoDB\\BSON\\Persistable +` interface are serialized to and deserialized from +BSON. The :php:`Persistable ` interface is analogous +to PHP's :php:`Serializable interface `. + +The driver automatically handles serialization and deserialization for classes +implementing the :php:`Persistable ` interface without +requiring the use of the ``typeMap`` option. This is done by encoding the name +of the PHP class in a special property within the BSON document. + +.. note:: + + When deserializing a PHP variable from BSON, the encoded class name of a + :php:`Persistable ` object will override any class + specified in the type map, but it will not override ``"array"`` and + ``"stdClass"`` or ``"object"``. This is discussed in the + :php:`persistence specification ` but it bears + repeating. + +Consider the following class definition: + +.. code-block:: php + + id = new MongoDB\BSON\ObjectId; + $this->name = $name; + $this->createdAt = new MongoDB\BSON\UTCDateTime; + } + + function bsonSerialize() + { + return [ + '_id' => $this->id, + 'name' => $this->name, + 'createdAt' => $this->createdAt, + ]; + } + + function bsonUnserialize(array $data) + { + $this->id = $data['_id']; + $this->name = $data['name']; + $this->createdAt = $data['createdAt']; + } + } + +The following example constructs a ``Person`` object, inserts it into the +database, and reads it back as an object of the same type: + +.. code-block:: php + + test->persons; + + $result = $collection->insertOne(new Person('Bob')); + + $person = $collection->findOne(['_id' => $result->getInsertedId()]); + + var_dump($person); + +The output would then resemble: + +.. code-block:: none + + object(Person)#18 (3) { + ["id":"Person":private]=> + object(MongoDB\BSON\ObjectId)#15 (1) { + ["oid"]=> + string(24) "56fad2c36118fd2e9820cfc1" + } + ["name":"Person":private]=> + string(3) "Bob" + ["createdAt":"Person":private]=> + object(MongoDB\BSON\UTCDateTime)#17 (1) { + ["milliseconds"]=> + int(1459278531218) + } + } + +The same document in the MongoDB shell might display as: + +.. code-block:: js + + { + "_id" : ObjectId("56fad2c36118fd2e9820cfc1"), + "__pclass" : BinData(128,"UGVyc29u"), + "name" : "Bob", + "createdAt" : ISODate("2016-03-29T19:08:51.218Z") + } + +.. note:: + + :php:`MongoDB\\BSON\\Persistable ` may only be used + for root and embedded BSON documents. It may not be used for BSON arrays. +.. _php-type-map: + +Working with Enums +------------------ + +:php:`Backed enums ` can be used with BSON and will +serialize as their case value (i.e. integer or string). +:php:`Pure enums `, which have no backed cases, cannot be +directly serialized. This is similar to how enums are handled by +:php:`json_encode() `. + +Round-tripping a backed enum through BSON requires special handling. In the +following example, the ``bsonUnserialize()`` method in the class containing the +enum is responsible for converting the value back to an enum case: + +.. code-block:: php + + $this->_id, + 'username' => $this->username, + 'role' => $this->role, + ]; + } + + public function bsonUnserialize(array $data): void + { + $this->_id = $data['_id']; + $this->username = $data['username']; + $this->role = Role::from($data['role']); + } + } + +Enums are prohibited from implementing +:php:`MongoDB\\BSON\\Unserializable ` and +:php:`MongoDB\\BSON\\Persistable `, since enum cases +have no state and cannot be instantiated like ordinary objects. Pure and backed +enums can, however, implement +:php:`MongoDB\\BSON\\Serializable `, which can be +used to overcome the default behavior whereby backed enums are serialized as +their case value and pure enums cannot be serialized. diff --git a/docs/tutorial/server-selection.txt b/docs/tutorial/server-selection.txt new file mode 100644 index 000000000..31047d936 --- /dev/null +++ b/docs/tutorial/server-selection.txt @@ -0,0 +1,192 @@ +=============================== +Server Selection and Monitoring +=============================== + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Server Selection and Monitoring +------------------------------- + +Before any operation can be executed, the |php-library| must first select a +server from the topology (e.g. replica set, sharded cluster). Selecting a server +requires an accurate view of the topology, so the driver (i.e. ``mongodb`` +extension) regularly monitors the servers to which it is connected. + +In most other drivers, server discovery and monitoring is handled by a +background thread; however, the PHP driver is single-threaded and must therefore +perform monitoring *between* operations initiated by the application. + +Consider the following example application: + +.. code-block:: php + + test; + + /** + * The library creates an internal object for this operation and must select + * a server to use for executing that operation. + * + * If this is the first operation on the underlying libmongoc client, it must + * first discover the topology. It does so by establishing connections to any + * host(s) in the seed list (this may entail TLS and OCSP verification) and + * issuing "hello" commands. + * + * In the case of a replica set, connecting to a single host in the seed list + * should allow the driver to discover all other members in the replica set. + * In the case of a sharded cluster, the driver will start with an initial + * seed list of mongos hosts and, if SRV polling is utilized, may discover + * additional mongos hosts over time. + * + * If the topology was already initialized (i.e. this is not the first + * operation on the client), the driver may still need to perform monitoring + * (i.e. "hello" commands) and refresh its view of the topology. This process + * may entail adding or removing hosts from the topology. + * + * Once the topology has been discovered and any necessary monitoring has + * been performed, the driver may select a server according to the rules + * outlined in the server selection specification (e.g. applying a read + * preference, filtering hosts by latency). + */ + $database->command(['ping' => 1]); + +Although the application consists of only a few lines of PHP, there is actually +quite a lot going on behind the scenes! Interested readers can find this process +discussed in greater detail in the following documents: + +- `Single-threaded Mode `_ in the libmongoc documentation +- `Server Discovery and Monitoring `_ specification +- `Server Selection `_ specification + +Connection String Options +------------------------- + +There are several connection string options relevant to server selection and +monitoring. + +connectTimeoutMS +~~~~~~~~~~~~~~~~ + +``connectTimeoutMS`` specifies the limit for both establishing a connection to +a server *and* the socket timeout for server monitoring (``hello`` commands). +This defaults to 10 seconds for single-threaded drivers such as PHP. + +When a server times out during monitoring, it will not be re-checked until at +least five seconds +(`cooldownMS `_) +have elapsed. This timeout is intended to avoid having single-threaded drivers +block for ``connectTimeoutMS`` on *each* subsequent scan after an error. + +Applications can consider setting this option to slightly more than the greatest +latency among servers in the cluster. For example, if the greatest ``ping`` time +between the PHP application server and a database server is 200ms, it may be +reasonable to specify a timeout of one second. This would allow ample time for +establishing a connection and monitoring an accessible server, while also +significantly reducing the time to detect an inaccessible server. + +heartbeatFrequencyMS +~~~~~~~~~~~~~~~~~~~~ + +``heartbeatFrequencyMS`` determines how often monitoring should occur. This +defaults to 60 seconds for single-threaded drivers and can be set as low as +500ms. + +serverSelectionTimeoutMS +~~~~~~~~~~~~~~~~~~~~~~~~ + +``serverSelectionTimeoutMS`` determines the maximum amount of time to spend in +the server selection loop. This defaults to 30 seconds, but applications will +typically fail sooner if ``serverSelectionTryOnce`` is ``true`` and a smaller +``connectTimeoutMS`` value is in effect. + +The original default was established at a time when replica set elections took +much longer to complete. Applications can consider setting this option to +slightly more than the expected completion time for an election. For example, +:manual:`Replica Set Elections ` states that +elections will not typically exceed 12 seconds, so a 15-second timeout may be +reasonable. Applications connecting to a sharded cluster may consider a smaller +value still, since ``mongos`` insulates the driver from elections. + +That said, ``serverSelectionTimeoutMS`` should generally not be set to a value +smaller than ``connectTimeoutMS``. + +serverSelectionTryOnce +~~~~~~~~~~~~~~~~~~~~~~ + +``serverSelectionTryOnce`` determines whether the driver should give up after +the first failed server selection attempt or continue waiting until +``serverSelectionTimeoutMS`` is reached. PHP defaults to ``true``, which allows +the driver to "fail fast" when a server cannot be selected (e.g. no primary +during a failover). + +The default behavior is generally desirable for a high-traffic web applications, +as it means the worker process will not be blocked in a server selection loop +and can instead return an error response and immediately go on to serve another +request. Additionally, other driver features such as retryable reads and writes +can still enable applications to avoid transient errors such as a failover. + +That said, applications that prioritize resiliency over response time (and +worker pool utilization) may want to specify ``false`` for +``serverSelectionTryOnce``. + +socketCheckIntervalMS +~~~~~~~~~~~~~~~~~~~~~ + +``socketCheckIntervalMS`` determines how often a socket should be checked (using +a ``ping`` command) if it has not been used recently. This defaults to 5 seconds +and is intentionally lower than ``heartbeatFrequencyMS`` to better allow +single-threaded drivers to recover dropped connections. + +socketTimeoutMS +~~~~~~~~~~~~~~~ + +``socketTimeoutMS`` determines the maximum amount of time to spend reading or +writing to a socket. Since server monitoring uses ``connectTimeoutMS`` for its +socket timeouts, ``socketTimeoutMS`` only applies to operations executed by the +application. + +``socketTimeoutMS`` defaults to 5 minutes; however, it's likely that a PHP web +request would be terminated sooner due to +`max_execution_time `_, +which defaults to 30 seconds for web SAPIs. In a CLI environment, where +``max_execution_time`` is unlimited by default, it is more likely that +``socketTimeoutMS`` could be reached. + +.. note:: + + ``socketTimeoutMS`` is not directly related to server selection and + monitoring; however, it is frequently associated with the other options and + therefore bears mentioning. diff --git a/docs/tutorial/stable-api.txt b/docs/tutorial/stable-api.txt new file mode 100644 index 000000000..1220f0326 --- /dev/null +++ b/docs/tutorial/stable-api.txt @@ -0,0 +1,93 @@ +========== +Stable API +========== + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Declaring an API Version +------------------------ + +To declare an API version, pass a ``serverApi`` driver option when creating your +client. The value is a +:php:`MongoDB\\Driver\\ServerApi ` instance that +contains API version information. This feature is introduced in MongoDB 5.0, +which will initially support only API version "1". Additional versions may be +introduced in future versions of the server. + +.. code-block:: php + + $serverApi]); + + // Command includes the declared API version + $client->database->collection->find([]); + +.. note:: + + Only declare an API version when connecting to a deployment that has no + pre-5.0 members. Older servers will error when encountering commands with a + declared API version. + +Strict API +---------- + +By default, declaring an API version guarantees behavior for commands that are +part of the stable API, but does not forbid using commands that are not part +of the API version. To only allow commands and options that are part of the +stable API, specify the ``strict`` option when creating the +:php:`MongoDB\\Driver\\ServerApi ` instance: + +.. code-block:: php + + $serverApi]); + + // Will fail as the tailable option is not supported in versioned API + $client->database->collection->find([], ['tailable' => true]); + +Fail on Deprecated Commands +--------------------------- + +The optional ``deprecationErrors`` option causes MongoDB to fail all commands +or behaviors that have been deprecated in the API version. This can be used in +testing to ensure a smooth transition to a future API version. + +.. code-block:: php + + $serverApi]); + +.. note:: + + At the time of this writing, no part of API version "1" has been deprecated. + +Usage with the Command Helper +----------------------------- + +When using the :phpmethod:`MongoDB\\Database::command()` method to run arbitrary +commands, the API version declared to the client is automatically appended to +the command document. Setting any of the ``apiVersion``, ``apiStrict``, or +``apiDeprecationErrors`` command options in the command document and calling +:phpmethod:`MongoDB\\Database::command()` from a client with a declared API +version is not supported and will lead to undefined behavior. diff --git a/docs/tutorial/tailable-cursor.txt b/docs/tutorial/tailable-cursor.txt index c4ff3b0eb..370a642a1 100644 --- a/docs/tutorial/tailable-cursor.txt +++ b/docs/tutorial/tailable-cursor.txt @@ -18,7 +18,7 @@ Overview When the driver executes a query or command (e.g. :manual:`aggregate `), results from the operation are returned via a :php:`MongoDB\\Driver\\Cursor ` -object. The Cursor class implements PHP's :php:`Traversable ` +object. The Cursor class implements PHP's :php:`Iterator ` interface, which allows it to be iterated with ``foreach`` and interface with any PHP functions that work with :php:`iterables `. Similar to result objects in other database drivers, cursors in MongoDB only support @@ -35,20 +35,21 @@ While normal cursors can be iterated once with ``foreach``, that approach will not work with tailable cursors. When ``foreach`` is used with a tailable cursor, the loop will stop upon reaching the end of the initial result set. Attempting to continue iteration on the cursor with a second ``foreach`` would throw an -exception, since PHP attempts to rewind the cursor. +exception, since PHP attempts to rewind the cursor. Therefore, reading from a +tailable cursor will require direct usage of the :php:`Iterator ` API. -In order to continuously read from a tailable cursor, we will need to wrap the -Cursor object with an :php:`IteratorIterator `. This will -allow us to directly control the cursor's iteration (e.g. call ``next()``), -avoid inadvertently rewinding the cursor, and decide when to wait for new -results or stop iteration entirely. +.. note:: + + Before version 1.9.0 of the ``ext-mongodb`` extension, the cursor class does + not implement the :php:`Iterator ` interface. To manually iterate + a cursor using the method below, it must first be wrapped with an + :php:`IteratorIterator `. -Wrapping a Normal Cursor ------------------------- +Manually Iterating a Normal Cursor +---------------------------------- -Before looking at how a tailable cursor can be wrapped with -:php:`IteratorIterator `, we'll start by examining how the -class interacts with a normal cursor. +Before looking at how a tailable cursor can be iterated, we'll start by +examining how the ``Iterator`` methods interact with a normal cursor. The following example finds five restaurants and uses ``foreach`` to view the results: @@ -74,7 +75,7 @@ not occurred, the iterator then advances to the next position, control jumps back to the validity check, and the loop continues. With the inner workings of ``foreach`` under our belt, we can now translate the -preceding example to use IteratorIterator: +preceding example to use the Iterator methods directly: .. code-block:: php @@ -84,28 +85,26 @@ preceding example to use IteratorIterator: $cursor = $collection->find([], ['limit' => 5]); - $iterator = new IteratorIterator($cursor); + $cursor->rewind(); - $iterator->rewind(); - - while ($iterator->valid()) { - $document = $iterator->current(); + while ($cursor->valid()) { + $document = $cursor->current(); var_dump($document); - $iterator->next(); + $cursor->next(); } .. note:: - Calling ``$iterator->next()`` after the ``while`` loop naturally ends would + Calling ``$cursor->next()`` after the ``while`` loop naturally ends would throw an exception, since all results on the cursor have been exhausted. -The purpose of this example is simply to demonstrate the functional equivalence -between ``foreach`` and manual iteration with PHP's :php:`Iterator ` -API. For normal cursors, there is little reason to use IteratorIterator instead +The purpose of this example is to demonstrate the functional equivalence between +``foreach`` and manual iteration with PHP's :php:`Iterator `API. +For normal cursors, there is little reason to manually iterate results instead of a concise ``foreach`` loop. -Wrapping a Tailable Cursor --------------------------- +Iterating a Tailable Cursor +--------------------------- In order to demonstrate a tailable cursor in action, we'll need two scripts: a "producer" and a "consumer". The producer script will create a new capped @@ -154,7 +153,7 @@ If you execute this consumer script, you'll notice that it quickly exhausts all results in the capped collection and then terminates. We cannot add a second ``foreach``, as that would throw an exception when attempting to rewind the cursor. This is a ripe use case for directly controlling the iteration process -using :php:`IteratorIterator `. +using the :php:`Iterator ` interface. .. code-block:: php @@ -167,17 +166,15 @@ using :php:`IteratorIterator `. 'maxAwaitTimeMS' => 100, ]); - $iterator = new IteratorIterator($cursor); - - $iterator->rewind(); + $cursor->rewind(); while (true) { - if ($iterator->valid()) { - $document = $iterator->current(); + if ($cursor->valid()) { + $document = $cursor->current(); printf("Consumed document created at: %s\n", $document->createdAt); } - $iterator->next(); + $cursor->next(); } Much like the ``foreach`` example, this version on the consumer script will diff --git a/docs/upgrade.txt b/docs/upgrade.txt index d783f13ae..50da6f2fe 100644 --- a/docs/upgrade.txt +++ b/docs/upgrade.txt @@ -1,6 +1,6 @@ -============= -Upgrade Guide -============= +=========================== +Legacy Driver Upgrade Guide +=========================== .. default-domain:: mongodb @@ -14,23 +14,24 @@ Overview -------- The |php-library| and underlying :php:`mongodb extension ` have notable -API differences from the legacy :php:`mongo extension `. This page will -summarize those differences for the benefit of those upgrading from the legacy -driver. +API differences from the legacy ``mongo`` extension. This page will summarize +those differences for the benefit of those upgrading from the legacy driver. Additionally, a community-developed `mongo-php-adapter `_ library exists, which -implements the `mongo extension `_ API using this library -and the new driver. While this adapter library is not officially supported by -MongoDB, it does bear mentioning. +implements the ``mongo`` extension API using this library and the new driver. +While this adapter library is not officially supported by MongoDB, it does bear +mentioning. -BSON Type Classes ------------------ +BSON +---- -When upgrading from the legacy driver, -`type classes `_ such as -:php:`MongoId ` must be replaced with classes in the -`MongoDB\\BSON namespace `_. The +Type Classes +~~~~~~~~~~~~ + +When upgrading from the legacy driver, type classes such as MongoId must be +replaced with classes in the +`MongoDB\\BSON namespace `_. The new driver also introduces interfaces for its BSON types, which should be preferred if applications need to type hint against BSON values. @@ -44,54 +45,54 @@ the new driver. - BSON type class - BSON type interface - * - :php:`MongoId ` + * - MongoId - :php:`MongoDB\\BSON\\ObjectId ` - :php:`MongoDB\\BSON\\ObjectIdInterface ` - * - :php:`MongoCode ` + * - MongoCode - :php:`MongoDB\\BSON\\Javascript ` - :php:`MongoDB\\BSON\\JavascriptInterface ` - * - :php:`MongoDate ` + * - MongoDate - :php:`MongoDB\\BSON\\UTCDateTime ` - :php:`MongoDB\\BSON\\UTCDateTimeInterface ` - * - :php:`MongoRegex ` + * - MongoRegex - :php:`MongoDB\\BSON\\Regex ` - :php:`MongoDB\\BSON\\RegexInterface ` - * - :php:`MongoBinData ` + * - MongoBinData - :php:`MongoDB\\BSON\\Binary ` - :php:`MongoDB\\BSON\\BinaryInterface ` - * - :php:`MongoInt32 ` + * - MongoInt32 - Not implemented. [1]_ - - * - :php:`MongoInt64 ` + * - MongoInt64 - :php:`MongoDB\\BSON\\Int64 ` - Not implemented. [2]_ - * - :php:`MongoDBRef ` + * - MongoDBRef - Not implemented. [3]_ - - * - :php:`MongoMinKey ` + * - MongoMinKey - :php:`MongoDB\\BSON\\MinKey ` - :php:`MongoDB\\BSON\\MinKeyInterface ` - * - :php:`MongoMaxKey ` + * - MongoMaxKey - :php:`MongoDB\\BSON\\MaxKey ` - :php:`MongoDB\\BSON\\MaxKeyInterface ` - * - :php:`MongoTimestamp ` + * - MongoTimestamp - :php:`MongoDB\\BSON\\Timestamp ` - :php:`MongoDB\\BSON\\TimestampInterface ` -.. [1] The new driver does not implement an equivalent class for - :php:`MongoInt32 `. When decoding BSON, 32-bit integers will - always be represented as a PHP integer. When encoding BSON, PHP integers will - encode as either a 32-bit or 64-bit integer depending on their value. +.. [1] The new driver does not implement an equivalent class for MongoInt32. + When decoding BSON, 32-bit integers will always be represented as a PHP + integer. When encoding BSON, PHP integers will encode as either a 32-bit or + 64-bit integer depending on their value. .. [2] :php:`MongoDB\\BSON\\Int64 ` does not have an interface defined. The new driver does not allow applications to instantiate @@ -99,12 +100,75 @@ the new driver. BSON decoding when a 64-bit integer cannot be represented as a PHP integer on a 32-bit platform. -.. [3] The new driver does not implement an equivalent class for - :php:`MongoDBRef ` since - :manual:`DBRefs ` are merely a BSON document - with a particular structure and not a proper BSON type. The new driver also - does not provide any helpers for working with DBRef objects, since their use - is not encouraged. +.. [3] The new driver does not implement an equivalent class for MongoDBRef + since :manual:`DBRefs ` are merely a BSON + document with a particular structure and not a proper BSON type. The new + driver also does not provide any helpers for working with DBRef objects, + since their use is not encouraged. + +Emulating the Legacy Driver +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The legacy ``mongo`` extension returned both BSON documents and arrays as PHP +arrays. While PHP arrays are convenient to work with, this behavior was +problematic: + +- Different BSON types could deserialize to the same PHP value (e.g. + ``{"0": "foo"}`` and ``["foo"]``), which made it impossible to infer the + original BSON type. + +- Numerically-indexed PHP arrays would be serialized as BSON documents if there + was a gap in their key sequence. Such gaps were caused by unsetting a key to + remove an element and forgetting to numerically reindex the array. + +The |php-library|'s :phpclass:`BSONDocument ` and +:phpclass:`BSONArray ` classes address these concerns +by preserving the BSON type information during serialization and +deserialization; however, some users may still prefer the legacy behavior. If +desired, you can use the ``typeMap`` option to have the library return +everything as a PHP array: + +.. code-block:: php + + [ + 'array' => 'array', + 'document' => 'array', + 'root' => 'array', + ], + ] + ); + + $document = $client->test->zips->findOne(['_id' => '94301']); + + var_dump($document); + +The above example would output something similar to: + +.. code-block:: php + + array(5) { + ["_id"]=> + string(5) "94301" + ["city"]=> + string(9) "PALO ALTO" + ["loc"]=> + array(2) { + [0]=> + float(-122.149685) + [1]=> + float(37.444324) + } + ["pop"]=> + int(15965) + ["state"]=> + string(2) "CA" + } Collection API -------------- @@ -116,15 +180,13 @@ and `Index Management `_ specifications. Although some method names have changed in accordance with the new specifications, the new class provides the same functionality as the legacy -driver's :php:`MongoCollection ` class with some notable -exceptions. +driver's MongoCollection class with some notable exceptions. A guiding principle in designing the new APIs was that explicit method names are preferable to overloaded terms found in the old API. For instance, -:php:`MongoCollection::save() ` and -:php:`MongoCollection::findAndModify() ` -have different modes of operation, depending on their arguments. Methods were -also split to distinguish between :manual:`updating specific fields +``MongoCollection::save()`` and ``MongoCollection::findAndModify()`` have +different modes of operation, depending on their arguments. Methods were also +split to distinguish between :manual:`updating specific fields ` and :manual:`full-document replacement `. @@ -134,116 +196,115 @@ equivalent method(s) in the new driver. .. list-table:: :header-rows: 1 - * - :php:`MongoCollection ` method + * - MongoCollection method - :phpclass:`MongoDB\\Collection` method(s) - * - :php:`MongoCollection::aggregate() ` + * - ``MongoCollection::aggregate()`` - :phpmethod:`MongoDB\\Collection::aggregate()` - * - :php:`MongoCollection::aggregateCursor() ` + * - ``MongoCollection::aggregateCursor()`` - :phpmethod:`MongoDB\\Collection::aggregate()` - * - :php:`MongoCollection::batchInsert() ` + * - ``MongoCollection::batchInsert()`` - :phpmethod:`MongoDB\\Collection::insertMany()` - * - :php:`MongoCollection::count() ` + * - ``MongoCollection::count()`` - :phpmethod:`MongoDB\\Collection::count()` - * - :php:`MongoCollection::createDBRef() ` + * - ``MongoCollection::createDBRef()`` - Not yet implemented. [3]_ - * - :php:`MongoCollection::createIndex() ` + * - ``MongoCollection::createIndex()`` - :phpmethod:`MongoDB\\Collection::createIndex()` - * - :php:`MongoCollection::deleteIndex() ` + * - ``MongoCollection::deleteIndex()`` - :phpmethod:`MongoDB\\Collection::dropIndex()` - * - :php:`MongoCollection::deleteIndexes() ` + * - ``MongoCollection::deleteIndexes()`` - :phpmethod:`MongoDB\\Collection::dropIndexes()` - * - :php:`MongoCollection::drop() ` + * - ``MongoCollection::drop()`` - :phpmethod:`MongoDB\\Collection::drop()` - * - :php:`MongoCollection::distinct() ` + * - ``MongoCollection::distinct()`` - :phpmethod:`MongoDB\\Collection::distinct()` - * - :php:`MongoCollection::ensureIndex() ` + * - ``MongoCollection::ensureIndex()`` - :phpmethod:`MongoDB\\Collection::createIndex()` - * - :php:`MongoCollection::find() ` + * - ``MongoCollection::find()`` - :phpmethod:`MongoDB\\Collection::find()` - * - :php:`MongoCollection::findAndModify() ` + * - ``MongoCollection::findAndModify()`` - :phpmethod:`MongoDB\\Collection::findOneAndDelete()`, :phpmethod:`MongoDB\\Collection::findOneAndReplace()`, and :phpmethod:`MongoDB\\Collection::findOneAndUpdate()` - * - :php:`MongoCollection::findOne() ` + * - ``MongoCollection::findOne()`` - :phpmethod:`MongoDB\\Collection::findOne()` - * - :php:`MongoCollection::getDBRef() ` + * - ``MongoCollection::getDBRef()`` - Not implemented. [3]_ - * - :php:`MongoCollection::getIndexInfo() ` + * - ``MongoCollection::getIndexInfo()`` - :phpmethod:`MongoDB\\Collection::listIndexes()` - * - :php:`MongoCollection::getName() ` + * - ``MongoCollection::getName()`` - :phpmethod:`MongoDB\\Collection::getCollectionName()` - * - :php:`MongoCollection::getReadPreference() ` - - Not implemented. + * - ``MongoCollection::getReadPreference()`` + - :phpmethod:`MongoDB\\Collection::getReadPreference()` - * - :php:`MongoCollection::getSlaveOkay() ` + * - ``MongoCollection::getSlaveOkay()`` - Not implemented. - * - :php:`MongoCollection::getWriteConcern() ` - - Not implemented. + * - ``MongoCollection::getWriteConcern()`` + - :phpmethod:`MongoDB\\Collection::getWriteConcern()` - * - :php:`MongoCollection::group() ` + * - ``MongoCollection::group()`` - Not implemented. Use :phpmethod:`MongoDB\\Database::command()`. See - `Group Command Helper`_ for an example. + :ref:`Group Command Helper ` for an example. - * - :php:`MongoCollection::insert() ` + * - ``MongoCollection::insert()`` - :phpmethod:`MongoDB\\Collection::insertOne()` - * - :php:`MongoCollection::parallelCollectionScan() ` + * - ``MongoCollection::parallelCollectionScan()`` - Not implemented. - * - :php:`MongoCollection::remove() ` + * - ``MongoCollection::remove()`` - :phpmethod:`MongoDB\\Collection::deleteMany()` and :phpmethod:`MongoDB\\Collection::deleteOne()` - * - :php:`MongoCollection::save() ` + * - ``MongoCollection::save()`` - :phpmethod:`MongoDB\\Collection::insertOne()` or :phpmethod:`MongoDB\\Collection::replaceOne()` with the ``upsert`` option. - * - :php:`MongoCollection::setReadPreference() ` + * - ``MongoCollection::setReadPreference()`` - Not implemented. Use :phpmethod:`MongoDB\\Collection::withOptions()`. - * - :php:`MongoCollection::setSlaveOkay() ` + * - ``MongoCollection::setSlaveOkay()`` - Not implemented. - * - :php:`MongoCollection::setWriteConcern() ` + * - ``MongoCollection::setWriteConcern()`` - Not implemented. Use :phpmethod:`MongoDB\\Collection::withOptions()`. - * - :php:`MongoCollection::update() ` + * - ``MongoCollection::update()`` - :phpmethod:`MongoDB\\Collection::replaceOne()`, :phpmethod:`MongoDB\\Collection::updateMany()`, and :phpmethod:`MongoDB\\Collection::updateOne()`. - * - :php:`MongoCollection::validate() ` + * - ``MongoCollection::validate()`` - Not implemented. Accessing IDs of Inserted Documents ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In the legacy driver, :php:`MongoCollection::insert() `, -:php:`MongoCollection::batchInsert() `, and -:php:`MongoCollection::save() ` (when inserting) would -modify their input argument by injecting an ``_id`` key with a generated -ObjectId (i.e. :php:`MongoId ` object). This behavior was a bit -of a hack, as it did not rely on the argument being :php:`passed by reference +In the legacy driver, ``MongoCollection::insert()``, +``MongoCollection::batchInsert()``, and ``MongoCollection::save()`` (when +inserting) would modify their input argument by injecting an ``_id`` key with a +generated ObjectId (i.e. MongoId object). This behavior was a bit of a hack, as +it did not rely on the argument being :php:`passed by reference `; instead, it directly modified memory through the extension API and could not be implemented in PHP userland. As such, it is no longer done in the new driver and library. @@ -261,22 +322,21 @@ following methods on the write result objects: Bulk Write Operations ~~~~~~~~~~~~~~~~~~~~~ -The legacy driver's :php:`MongoWriteBatch ` classes have -been replaced with a general-purpose -:phpmethod:`MongoDB\\Collection::bulkWrite()` method. Whereas the legacy driver -only allowed bulk operations of the same type, the new method allows operations -to be mixed (e.g. inserts, updates, and deletes). +The legacy driver's MongoWriteBatch classes have been replaced with a +general-purpose :phpmethod:`MongoDB\\Collection::bulkWrite()` method. Whereas +the legacy driver only allowed bulk operations of the same type, the new method +allows operations to be mixed (e.g. inserts, updates, and deletes). MongoCollection::save() Removed ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -:php:`MongoCollection::save() `, which was syntactic sugar -for an insert or upsert operation, has been removed in favor of explicitly using -:phpmethod:`MongoDB\\Collection::insertOne` or -:phpmethod:`MongoDB\\Collection::replaceOne` (with the ``upsert`` option). +``MongoCollection::save()``, which was syntactic sugar for an insert or upsert +operation, has been removed in favor of explicitly using +:phpmethod:`MongoDB\\Collection::insertOne()` or +:phpmethod:`MongoDB\\Collection::replaceOne()` (with the ``upsert`` option). While the ``save`` method does have its uses for interactive environments, such -as the ``mongo`` shell, it was intentionally excluded from the +as the MongoDB shell, it was intentionally excluded from the `CRUD specification `_ for language drivers. Generally, application code should know if the document has an identifier and be able to explicitly insert or replace the document and @@ -285,10 +345,12 @@ handle the returned :phpclass:`MongoDB\\InsertOneResult` or inadvertent and potentially dangerous :manual:`full-document replacements `. +.. _group-command-helper: + Group Command Helper ~~~~~~~~~~~~~~~~~~~~ -:phpclass:`MongoDB\\Collection` does have a helper method for the +:phpclass:`MongoDB\\Collection` does not have a helper method for the :manual:`group ` command. The following example demonstrates how to execute a group command using the :phpmethod:`MongoDB\\Database::command()` method: diff --git a/examples/aggregate.php b/examples/aggregate.php new file mode 100644 index 000000000..d1ee69450 --- /dev/null +++ b/examples/aggregate.php @@ -0,0 +1,58 @@ +test->aggregate; +$collection->drop(); + +$documents = []; + +for ($i = 0; $i < 100; $i++) { + $documents[] = ['randomValue' => random_int(0, 1000)]; +} + +$collection->insertMany($documents); + +$pipeline = [ + [ + '$group' => [ + '_id' => null, + 'totalCount' => ['$sum' => 1], + 'evenCount' => [ + '$sum' => ['$mod' => ['$randomValue', 2]], + ], + 'oddCount' => [ + '$sum' => ['$subtract' => [1, ['$mod' => ['$randomValue', 2]]]], + ], + 'maxValue' => ['$max' => '$randomValue'], + 'minValue' => ['$min' => '$randomValue'], + ], + ], +]; + +$cursor = $collection->aggregate($pipeline); + +foreach ($cursor as $document) { + assert(is_object($document)); + printf("%s\n", toJSON($document)); +} diff --git a/examples/bulk.php b/examples/bulk.php new file mode 100644 index 000000000..316594f00 --- /dev/null +++ b/examples/bulk.php @@ -0,0 +1,79 @@ +test->bulk; +$collection->drop(); + +$documents = []; + +for ($i = 0; $i < 10; $i++) { + $documents[] = ['x' => $i]; +} + +$collection->insertMany($documents, ['writeConcern' => new WriteConcern('majority')]); + +$collection->bulkWrite( + [ + [ + 'deleteMany' => [ + ['x' => ['$gt' => 7]], // Filter + ], + ], + [ + 'deleteOne' => [ + ['x' => 4], // Filter + ], + ], + [ + 'replaceOne' => [ + ['x' => 1], // Filter + ['y' => 1], // Replacement + ], + ], + [ + 'updateMany' => [ + ['x' => ['$gt' => 5]], // Filter + ['$set' => ['updateMany' => true]], // Update + ], + ], + [ + 'updateOne' => [ + ['x' => 2], // Filter + ['$set' => ['y' => 2]], // Update + ], + ], + [ + 'insertOne' => [ + ['x' => 10], // Document + ], + ], + ] +); + +$cursor = $collection->find([]); + +foreach ($cursor as $document) { + assert(is_object($document)); + printf("%s\n", toJSON($document)); +} diff --git a/examples/changestream.php b/examples/changestream.php new file mode 100644 index 000000000..8c871f59c --- /dev/null +++ b/examples/changestream.php @@ -0,0 +1,59 @@ +test->changestream; +$collection->drop(); + +// Create collection before starting change stream; this is required on MongoDB 3.6 +$client->test->createCollection('changestream'); + +$changeStream = $collection->watch(); + +$documents = []; + +for ($i = 0; $i < 10; $i++) { + $documents[] = ['x' => $i]; +} + +$collection->insertMany($documents); + +$changeStream->rewind(); + +$startTime = time(); + +while (true) { + if ($changeStream->valid()) { + $event = $changeStream->current(); + assert(is_object($event)); + printf("%s\n", toJSON($event)); + } + + $changeStream->next(); + + if (time() - $startTime > 3) { + printf("Aborting after 3 seconds...\n"); + break; + } +} diff --git a/examples/command_logger.php b/examples/command_logger.php new file mode 100644 index 000000000..9d3f7c0fe --- /dev/null +++ b/examples/command_logger.php @@ -0,0 +1,80 @@ +getCommandName()); + + printf("command: %s\n", toJson($event->getCommand())); + printf("\n"); + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + printf("%s command succeeded\n", $event->getCommandName()); + printf("reply: %s\n", toJson($event->getReply())); + printf("\n"); + } + + public function commandFailed(CommandFailedEvent $event): void + { + printf("%s command failed\n", $event->getCommandName()); + printf("reply: %s\n", toJson($event->getReply())); + + $exception = $event->getError(); + printf("exception: %s\n", get_class($exception)); + printf("exception.code: %d\n", $exception->getCode()); + printf("exception.message: %s\n", $exception->getMessage()); + printf("\n"); + } +} + +$client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/'); + +$client->getManager()->addSubscriber(new CommandLogger()); + +$collection = $client->test->command_logger; +$collection->drop(); + +$collection->insertMany([ + ['x' => 1], + ['x' => 2], + ['x' => 3], +]); + +$collection->updateMany( + ['x' => ['$gt' => 1]], + ['$set' => ['y' => 1]] +); + +$cursor = $collection->find([], ['batchSize' => 2]); + +foreach ($cursor as $document) { + assert(is_object($document)); + printf("%s\n", toJSON($document)); +} diff --git a/examples/persistable.php b/examples/persistable.php new file mode 100644 index 000000000..6e23a16bb --- /dev/null +++ b/examples/persistable.php @@ -0,0 +1,108 @@ + */ + public $emails = []; + + public function __construct(string $name) + { + $this->id = new ObjectId(); + $this->name = $name; + } + + public function getId(): ObjectId + { + return $this->id; + } + + public function bsonSerialize(): object + { + return (object) [ + '_id' => $this->id, + 'name' => $this->name, + 'emails' => $this->emails, + ]; + } + + public function bsonUnserialize(array $data): void + { + if (! $data['_id'] instanceof ObjectId) { + throw new UnexpectedValueException('_id field is not of the expected type'); + } + + if (! $data['emails'] instanceof BSONArray) { + throw new UnexpectedValueException('emails field is not of the expected type'); + } + + $this->id = $data['_id']; + $this->name = (string) $data['name']; + + /** @psalm-suppress MixedPropertyTypeCoercion */ + $this->emails = $data['emails']->getArrayCopy(); // Emails will be passed as a BSONArray instance + } +} + +class PersistableEmail implements Persistable +{ + /** @var string */ + public $type; + + /** @var string */ + public $address; + + public function __construct(string $type, string $address) + { + $this->type = $type; + $this->address = $address; + } + + public function bsonSerialize(): object + { + return (object) [ + 'type' => $this->type, + 'address' => $this->address, + ]; + } + + public function bsonUnserialize(array $data): void + { + $this->type = (string) $data['type']; + $this->address = (string) $data['address']; + } +} + +$entry = new PersistableEntry('alcaeus'); +$entry->emails[] = new PersistableEmail('work', 'alcaeus@example.com'); +$entry->emails[] = new PersistableEmail('private', 'secret@example.com'); + +$client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/'); + +$collection = $client->test->persistable; +$collection->drop(); + +$collection->insertOne($entry); + +$foundEntry = $collection->findOne([]); + +print_r($foundEntry); diff --git a/examples/typemap.php b/examples/typemap.php new file mode 100644 index 000000000..9dc96b016 --- /dev/null +++ b/examples/typemap.php @@ -0,0 +1,119 @@ + */ + private $emails; + + private function __construct() + { + } + + public function getId(): ObjectId + { + return $this->id; + } + + public function getName(): string + { + return $this->name; + } + + public function getEmails(): array + { + return $this->emails; + } + + public function bsonUnserialize(array $data): void + { + if (! $data['_id'] instanceof ObjectId) { + throw new UnexpectedValueException('_id field is not of the expected type'); + } + + if (! is_array($data['emails'])) { + throw new UnexpectedValueException('emails field is not of the expected type'); + } + + $this->id = $data['_id']; + $this->name = (string) $data['name']; + + /** @psalm-suppress MixedPropertyTypeCoercion */ + $this->emails = $data['emails']; + } +} + +class TypeMapEmail implements Unserializable +{ + /** @var string */ + private $type; + + /** @var string */ + private $address; + + private function __construct() + { + } + + public function getType(): string + { + return $this->type; + } + + public function getAddress(): string + { + return $this->address; + } + + public function bsonUnserialize(array $data): void + { + $this->type = (string) $data['type']; + $this->address = (string) $data['address']; + } +} + +$client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/'); + +$collection = $client->test->typemap; +$collection->drop(); + +$document = [ + 'name' => 'alcaeus', + 'emails' => [ + ['type' => 'work', 'address' => 'alcaeus@example.com'], + ['type' => 'private', 'address' => 'secret@example.com'], + ], +]; + +$collection->insertOne($document); + +$typeMap = [ + 'root' => TypeMapEntry::class, // Root object will be an Entry instance + 'fieldPaths' => [ + 'emails' => 'array', // Emails field is used as PHP array + 'emails.$' => TypeMapEmail::class, // Each element in the emails array will be an Email instance + ], +]; + +$entry = $collection->findOne([], ['typeMap' => $typeMap]); + +print_r($entry); diff --git a/examples/with_transaction.php b/examples/with_transaction.php new file mode 100644 index 000000000..15eba6524 --- /dev/null +++ b/examples/with_transaction.php @@ -0,0 +1,59 @@ += 4.0) or sharded cluster (MongoDB >= 4.2) +$client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/'); + +$collection = $client->test->with_transaction; +$collection->drop(); + +// Create collection outside of transaction; this is required when using MongoDB < 4.4 +$client->test->createCollection('with_transaction'); + +$insertData = function (Session $session) use ($collection): void { + $collection->insertMany( + [ + ['x' => 1], + ['x' => 2], + ['x' => 3], + ], + ['session' => $session] + ); + + $collection->updateMany( + ['x' => ['$gt' => 1]], + ['$set' => ['y' => 1]], + ['session' => $session] + ); +}; + +$session = $client->startSession(); + +with_transaction($session, $insertData); + +$cursor = $collection->find([]); + +foreach ($cursor as $document) { + assert(is_object($document)); + printf("%s\n", toJSON($document)); +} diff --git a/mongo-orchestration/replica_sets/replicaset-old.json b/mongo-orchestration/replica_sets/replicaset-old.json deleted file mode 100644 index 87c81637c..000000000 --- a/mongo-orchestration/replica_sets/replicaset-old.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "id": "REPLICASET_OLD", - "name": "mongod", - "members": [ - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3500/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3500/mongod.log", - "journal": true, - "nssize": 1, - "port": 3500, - "bind_ip": "::,0.0.0.0", - "smallfiles": true - }, - "rsParams": { - "tags": { - "ordinal": "one", - "dc": "pa" - } - }, - "server_id": "RS-OLD-one" - }, - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3501/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3501/mongod.log", - "journal": true, - "nssize": 1, - "port": 3501, - "bind_ip": "::,0.0.0.0", - "smallfiles": true - }, - "rsParams": { - "tags": { - "ordinal": "two", - "dc": "nyc" - } - }, - "server_id": "RS-OLD-two" - }, - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3502/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3502/mongod.log", - "journal": true, - "nssize": 1, - "port": 3502, - "bind_ip": "::,0.0.0.0", - "smallfiles": true - }, - "rsParams": { - "arbiterOnly": true - - }, - "server_id": "RS-OLD-arbiter" - } - ] -} diff --git a/mongo-orchestration/replica_sets/replicaset-one-node.json b/mongo-orchestration/replica_sets/replicaset-one-node.json deleted file mode 100644 index 8b5bbd045..000000000 --- a/mongo-orchestration/replica_sets/replicaset-one-node.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "id": "REPLICASET_SINGLE", - "name": "mongod", - "members": [ - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3020/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3020/mongod.log", - "journal": true, - "port": 3020, - "bind_ip_all": true - }, - "rsParams": { - "tags": { - "ordinal": "one", - "dc": "pa" - } - }, - "server_id": "RS-alone" - } - ] -} diff --git a/mongo-orchestration/replica_sets/replicaset.json b/mongo-orchestration/replica_sets/replicaset.json deleted file mode 100644 index a80de8bbb..000000000 --- a/mongo-orchestration/replica_sets/replicaset.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "id": "REPLICASET", - "name": "mongod", - "members": [ - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3000/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3000/mongod.log", - "journal": true, - "port": 3000, - "bind_ip_all": true - }, - "rsParams": { - "tags": { - "ordinal": "one", - "dc": "pa" - } - }, - "server_id": "RS-one" - }, - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3001/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3001/mongod.log", - "journal": true, - "port": 3001, - "bind_ip_all": true - }, - "rsParams": { - "tags": { - "ordinal": "two", - "dc": "nyc" - } - }, - "server_id": "RS-two" - }, - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3002/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3002/mongod.log", - "journal": true, - "port": 3002, - "bind_ip_all": true - }, - "rsParams": { - "arbiterOnly": true - - }, - "server_id": "RS-arbiter" - } - ] -} diff --git a/mongo-orchestration/sharded_clusters/cluster.json b/mongo-orchestration/sharded_clusters/cluster.json deleted file mode 100644 index 094785a7e..000000000 --- a/mongo-orchestration/sharded_clusters/cluster.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "configsvrs": [ { - "members" : [ - { - "procParams": { - "dbpath": "/tmp/SHARDED/CFG/4000", - "logpath": "/tmp/SHARDED/CFG/4000/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4000, - "bind_ip_all": true - } - }, - { - "procParams": { - "dbpath": "/tmp/SHARDED/CFG/4001", - "logpath": "/tmp/SHARDED/CFG/4001/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4001, - "bind_ip_all": true - } - }, - { - "procParams": { - "dbpath": "/tmp/SHARDED/CFG/4002", - "logpath": "/tmp/SHARDED/CFG/4002/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4002, - "bind_ip_all": true - } - } - ] - } ], - "id": "cluster_1", - "shards": [ - { - "id": "sh01", - "shardParams": { - "procParams": { - "dbpath": "/tmp/SHARDED/SHARD1/4100", - "logpath": "/tmp/SHARDED/SHARD1/4100/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4100, - "bind_ip_all": true - } - } - }, - { - "id": "sh02", - "shardParams": { - "procParams": { - "dbpath": "/tmp/SHARDED/SHARD2/4200", - "logpath": "/tmp/SHARDED/SHARD2/4200/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4200, - "bind_ip_all": true - } - } - } - ], - "routers": [ - { - "logpath": "/tmp/SHARDED/ROUTER/4300/mongod.log", - "ipv6": true, - "logappend": true, - "port": 4300, - "bind_ip_all": true - }, - { - "logpath": "/tmp/SHARDED/ROUTER/4301/mongod.log", - "ipv6": true, - "logappend": true, - "port": 4301, - "bind_ip_all": true - } - ] -} diff --git a/mongo-orchestration/sharded_clusters/cluster_replset.json b/mongo-orchestration/sharded_clusters/cluster_replset.json deleted file mode 100644 index 80cd4c2bd..000000000 --- a/mongo-orchestration/sharded_clusters/cluster_replset.json +++ /dev/null @@ -1,126 +0,0 @@ -{ - "configsvrs": [ { - "members" : [ - { - "procParams": { - "dbpath": "/tmp/SHARDED-RS/CFG/4490", - "logpath": "/tmp/SHARDED-RS/CFG/4490/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4490, - "bind_ip_all": true - } - }, - { - "procParams": { - "dbpath": "/tmp/SHARDED-RS/CFG/4491", - "logpath": "/tmp/SHARDED-RS/CFG/4491/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4491, - "bind_ip_all": true - } - }, - { - "procParams": { - "dbpath": "/tmp/SHARDED-RS/CFG/4492", - "logpath": "/tmp/SHARDED-RS/CFG/4492/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4492, - "bind_ip_all": true - } - } - ] - } ], - "id": "cluster_rs", - "shards": [ - { - "id": "cluster-rs-sh01", - "shardParams": { - "id": "sh01-rs", - "members": [ - { "procParams": { - "dbpath": "/tmp/SHARDED-RS/SHARD1/4400", - "logpath": "/tmp/SHARDED-RS/SHARD1/4400/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4400, - "bind_ip_all": true, - "setParameter": { - "periodicNoopIntervalSecs": 1, - "writePeriodicNoops": true - } - } }, - { "procParams": { - "dbpath": "/tmp/SHARDED-RS/SHARD1/4401", - "logpath": "/tmp/SHARDED-RS/SHARD1/4401/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4401, - "bind_ip_all": true, - "setParameter": { - "periodicNoopIntervalSecs": 1, - "writePeriodicNoops": true - } - } } - ] - } - }, - { - "id": "cluster-rs-sh02", - "shardParams": { - "id": "sh02-rs", - "members": [ - { "procParams": { - "dbpath": "/tmp/SHARDED-RS/SHARD2/4410", - "logpath": "/tmp/SHARDED-RS/SHARD2/4410/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4410, - "bind_ip_all": true, - "setParameter": { - "periodicNoopIntervalSecs": 1, - "writePeriodicNoops": true - } - } }, - { "procParams": { - "dbpath": "/tmp/SHARDED-RS/SHARD2/4411", - "logpath": "/tmp/SHARDED-RS/SHARD2/4411/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4411, - "bind_ip_all": true, - "setParameter": { - "periodicNoopIntervalSecs": 1, - "writePeriodicNoops": true - } - } } - ] - } - } - ], - "routers": [ - { - "logpath": "/tmp/SHARDED-RS/ROUTER/4430/mongod.log", - "ipv6": true, - "logappend": true, - "port": 4430, - "bind_ip_all": true - }, - { - "logpath": "/tmp/SHARDED-RS/ROUTER/4431/mongod.log", - "ipv6": true, - "logappend": true, - "port": 4431, - "bind_ip_all": true - } - ] -} diff --git a/mongo-orchestration/ssl/ca.pem b/mongo-orchestration/ssl/ca.pem deleted file mode 100644 index 6ac86cfcc..000000000 --- a/mongo-orchestration/ssl/ca.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDfzCCAmegAwIBAgIDB1MGMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIwMjMxMVoXDTM5MDUyMjIwMjMxMVoweTEb -MBkGA1UEAxMSRHJpdmVycyBUZXN0aW5nIENBMRAwDgYDVQQLEwdEcml2ZXJzMRAw -DgYDVQQKEwdNb25nb0RCMRYwFAYDVQQHEw1OZXcgWW9yayBDaXR5MREwDwYDVQQI -EwhOZXcgWW9yazELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCl7VN+WsQfHlwapcOpTLZVoeMAl1LTbWTFuXSAavIyy0W1Ytky1UP/ -bxCSW0mSWwCgqoJ5aXbAvrNRp6ArWu3LsTQIEcD3pEdrFIVQhYzWUs9fXqPyI9k+ -QNNQ+MRFKeGteTPYwF2eVEtPzUHU5ws3+OKp1m6MCLkwAG3RBFUAfddUnLvGoZiT -pd8/eNabhgHvdrCw+tYFCWvSjz7SluEVievpQehrSEPKe8DxJq/IM3tSl3tdylzT -zeiKNO7c7LuQrgjAfrZl7n2SriHIlNmqiDR/kdd8+TxBuxjFlcf2WyHCO3lIcIgH -KXTlhUCg50KfHaxHu05Qw0x8869yIzqbAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8w -DQYJKoZIhvcNAQELBQADggEBAEHuhTL8KQZcKCTSJbYA9MgZj7U32arMGBbc1hiq -VBREwvdVz4+9tIyWMzN9R/YCKmUTnCq8z3wTlC8kBtxYn/l4Tj8nJYcgLJjQ0Fwe -gT564CmvkUat8uXPz6olOCdwkMpJ9Sj62i0mpgXJdBfxKQ6TZ9yGz6m3jannjZpN -LchB7xSAEWtqUgvNusq0dApJsf4n7jZ+oBZVaQw2+tzaMfaLqHgMwcu1FzA8UKCD -sxCgIsZUs8DdxaD418Ot6nPfheOTqe24n+TTa+Z6O0W0QtnofJBx7tmAo1aEc57i -77s89pfwIJetpIlhzNSMKurCAocFCJMJLAASJFuu6dyDvPo= ------END CERTIFICATE----- \ No newline at end of file diff --git a/mongo-orchestration/ssl/client.pem b/mongo-orchestration/ssl/client.pem deleted file mode 100644 index 5b0700109..000000000 --- a/mongo-orchestration/ssl/client.pem +++ /dev/null @@ -1,48 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAsNS8UEuin7/K29jXfIOLpIoh1jEyWVqxiie2Onx7uJJKcoKo -khA3XeUnVN0k6X5MwYWcN52xcns7LYtyt06nRpTG2/emoV44w9uKTuHsvUbiOwSV -m/ToKQQ4FUFZoqorXH+ZmJuIpJNfoW+3CkE1vEDCIecIq6BNg5ySsPtvSuSJHGjp -mc7/5ZUDvFE2aJ8QbJU3Ws0HXiEb6ymi048LlzEL2VKX3w6mqqh+7dcZGAy7qYk2 -5FZ9ktKvCeQau7mTyU1hsPrKFiKtMN8Q2ZAItX13asw5/IeSTq2LgLFHlbj5Kpq4 -GmLdNCshzH5X7Ew3IYM8EHmsX8dmD6mhv7vpVwIDAQABAoIBABOdpb4qhcG+3twA -c/cGCKmaASLnljQ/UU6IFTjrsjXJVKTbRaPeVKX/05sgZQXZ0t3s2mV5AsQ2U1w8 -Cd+3w+qaemzQThW8hAOGCROzEDX29QWi/o2sX0ydgTMqaq0Wv3SlWv6I0mGfT45y -/BURIsrdTCvCmz2erLqa1dL4MWJXRFjT9UTs5twlecIOM2IHKoGGagFhymRK4kDe -wTRC9fpfoAgyfus3pCO/wi/F8yKGPDEwY+zgkhrJQ+kSeki7oKdGD1H540vB8gRt -EIqssE0Y6rEYf97WssQlxJgvoJBDSftOijS6mwvoasDUwfFqyyPiirawXWWhHXkc -DjIi/XECgYEA5xfjilw9YyM2UGQNESbNNunPcj7gDZbN347xJwmYmi9AUdPLt9xN -3XaMqqR22k1DUOxC/5hH0uiXir7mDfqmC+XS/ic/VOsa3CDWejkEnyGLiwSHY502 -wD/xWgHwUiGVAG9HY64vnDGm6L3KGXA2oqxanL4V0+0+Ht49pZ16i8sCgYEAw+Ox -CHGtpkzjCP/z8xr+1VTSdpc/4CP2HONnYopcn48KfQnf7Nale69/1kZpypJlvQSG -eeA3jMGigNJEkb8/kaVoRLCisXcwLc0XIfCTeiK6FS0Ka30D/84Qm8UsHxRdpGkM -kYITAa2r64tgRL8as4/ukeXBKE+oOhX43LeEfyUCgYBkf7IX2Ndlhsm3GlvIarxy -NipeP9PGdR/hKlPbq0OvQf9R1q7QrcE7H7Q6/b0mYNV2mtjkOQB7S2WkFDMOP0P5 -BqDEoKLdNkV/F9TOYH+PCNKbyYNrodJOt0Ap6Y/u1+Xpw3sjcXwJDFrO+sKqX2+T -PStG4S+y84jBedsLbDoAEwKBgQCTz7/KC11o2yOFqv09N+WKvBKDgeWlD/2qFr3w -UU9K5viXGVhqshz0k5z25vL09Drowf1nAZVpFMO2SPOMtq8VC6b+Dfr1xmYIaXVH -Gu1tf77CM9Zk/VSDNc66e7GrUgbHBK2DLo+A+Ld9aRIfTcSsMbNnS+LQtCrQibvb -cG7+MQKBgQCY11oMT2dUekoZEyW4no7W5D74lR8ztMjp/fWWTDo/AZGPBY6cZoZF -IICrzYtDT/5BzB0Jh1f4O9ZQkm5+OvlFbmoZoSbMzHL3oJCBOY5K0/kdGXL46WWh -IRJSYakNU6VIS7SjDpKgm9D8befQqZeoSggSjIIULIiAtYgS80vmGA== ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDgzCCAmugAwIBAgIDAxOUMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIzNTU1NFoXDTM5MDUyMjIzNTU1NFowaTEP -MA0GA1UEAxMGY2xpZW50MRAwDgYDVQQLEwdEcml2ZXJzMQwwCgYDVQQKEwNNREIx -FjAUBgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3JrMQswCQYD -VQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALDUvFBLop+/ -ytvY13yDi6SKIdYxMllasYontjp8e7iSSnKCqJIQN13lJ1TdJOl+TMGFnDedsXJ7 -Oy2LcrdOp0aUxtv3pqFeOMPbik7h7L1G4jsElZv06CkEOBVBWaKqK1x/mZibiKST -X6FvtwpBNbxAwiHnCKugTYOckrD7b0rkiRxo6ZnO/+WVA7xRNmifEGyVN1rNB14h -G+spotOPC5cxC9lSl98Opqqofu3XGRgMu6mJNuRWfZLSrwnkGru5k8lNYbD6yhYi -rTDfENmQCLV9d2rMOfyHkk6ti4CxR5W4+SqauBpi3TQrIcx+V+xMNyGDPBB5rF/H -Zg+pob+76VcCAwEAAaMkMCIwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUF -BwMCMA0GCSqGSIb3DQEBCwUAA4IBAQAqRcLAGvYMaGYOV4HJTzNotT2qE0I9THNQ -wOV1fBg69x6SrUQTQLjJEptpOA288Wue6Jt3H+p5qAGV5GbXjzN/yjCoItggSKxG -Xg7279nz6/C5faoIKRjpS9R+MsJGlttP9nUzdSxrHvvqm62OuSVFjjETxD39DupE -YPFQoHOxdFTtBQlc/zIKxVdd20rs1xJeeU2/L7jtRBSPuR/Sk8zot7G2/dQHX49y -kHrq8qz12kj1T6XDXf8KZawFywXaz0/Ur+fUYKmkVk1T0JZaNtF4sKqDeNE4zcns -p3xLVDSl1Q5Gwj7bgph9o4Hxs9izPwiqjmNaSjPimGYZ399zcurY ------END CERTIFICATE----- diff --git a/mongo-orchestration/ssl/crl.pem b/mongo-orchestration/ssl/crl.pem deleted file mode 100644 index 733a0acdc..000000000 --- a/mongo-orchestration/ssl/crl.pem +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN X509 CRL----- -MIIB6jCB0wIBATANBgkqhkiG9w0BAQsFADB5MRswGQYDVQQDExJEcml2ZXJzIFRl -c3RpbmcgQ0ExEDAOBgNVBAsTB0RyaXZlcnMxEDAOBgNVBAoTB01vbmdvREIxFjAU -BgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3JrMQswCQYDVQQG -EwJVUxcNMTkwNTIyMjI0NTUzWhcNMTkwNjIxMjI0NTUzWjAVMBMCAncVFw0xOTA1 -MjIyMjQ1MzJaoA8wDTALBgNVHRQEBAICEAAwDQYJKoZIhvcNAQELBQADggEBACwQ -W9OF6ExJSzzYbpCRroznkfdLG7ghNSxIpBQUGtcnYbkP4em6TdtAj5K3yBjcKn4a -hnUoa5EJGr2Xgg0QascV/1GuWEJC9rsYYB9boVi95l1CrkS0pseaunM086iItZ4a -hRVza8qEMBc3rdsracA7hElYMKdFTRLpIGciJehXzv40yT5XFBHGy/HIT0CD50O7 -BDOHzA+rCFCvxX8UY9myDfb1r1zUW7Gzjn241VT7bcIJmhFE9oV0popzDyqr6GvP -qB2t5VmFpbnSwkuc4ie8Jizip1P8Hg73lut3oVAHACFGPpfaNIAp4GcSH61zJmff -9UBe3CJ1INwqyiuqGeA= ------END X509 CRL----- diff --git a/mongo-orchestration/ssl/server.pem b/mongo-orchestration/ssl/server.pem deleted file mode 100644 index 7480f9644..000000000 --- a/mongo-orchestration/ssl/server.pem +++ /dev/null @@ -1,49 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAhNrB0E6GY/kFSd8/vNpu/t952tbnOsD5drV0XPvmuy7SgKDY -a/S+xb/jPnlZKKehdBnH7qP/gYbv34ZykzcDFZscjPLiGc2cRGP+NQCSFK0d2/7d -y15zSD3zhj14G8+MkpAejTU+0/qFNZMc5neDvGanTe0+8aWa0DXssM0MuTxIv7j6 -CtsMWeqLLofN7a1Kw2UvmieCHfHMuA/08pJwRnV/+5T9WONBPJja2ZQRrG1BjpI4 -81zSPUZesIqi8yDlExdvgNaRZIEHi/njREqwVgJOZomUY57zmKypiMzbz48dDTsV -gUStxrEqbaP+BEjQYPX5+QQk4GdMjkLf52LR6QIDAQABAoIBAHSs+hHLJNOf2zkp -S3y8CUblVMsQeTpsR6otaehPgi9Zy50TpX4KD5D0GMrBH8BIl86y5Zd7h+VlcDzK -gs0vPxI2izhuBovKuzaE6rf5rFFkSBjxGDCG3o/PeJOoYFdsS3RcBbjVzju0hFCs -xnDQ/Wz0anJRrTnjyraY5SnQqx/xuhLXkj/lwWoWjP2bUqDprnuLOj16soNu60Um -JziWbmWx9ty0wohkI/8DPBl9FjSniEEUi9pnZXPElFN6kwPkgdfT5rY/TkMH4lsu -ozOUc5xgwlkT6kVjXHcs3fleuT/mOfVXLPgNms85JKLucfd6KiV7jYZkT/bXIjQ+ -7CZEn0ECgYEA5QiKZgsfJjWvZpt21V/i7dPje2xdwHtZ8F9NjX7ZUFA7mUPxUlwe -GiXxmy6RGzNdnLOto4SF0/7ebuF3koO77oLup5a2etL+y/AnNAufbu4S5D72sbiz -wdLzr3d5JQ12xeaEH6kQNk2SD5/ShctdS6GmTgQPiJIgH0MIdi9F3v0CgYEAlH84 -hMWcC+5b4hHUEexeNkT8kCXwHVcUjGRaYFdSHgovvWllApZDHSWZ+vRcMBdlhNPu -09Btxo99cjOZwGYJyt20QQLGc/ZyiOF4ximQzabTeFgLkTH3Ox6Mh2Rx9yIruYoX -nE3UfMDkYELanEJUv0zenKpZHw7tTt5yXXSlEF0CgYBSsEOvVcKYO/eoluZPYQAA -F2jgzZ4HeUFebDoGpM52lZD+463Dq2hezmYtPaG77U6V3bUJ/TWH9VN/Or290vvN -v83ECcC2FWlSXdD5lFyqYx/E8gqE3YdgqfW62uqM+xBvoKsA9zvYLydVpsEN9v8m -6CSvs/2btA4O21e5u5WBTQKBgGtAb6vFpe0gHRDs24SOeYUs0lWycPhf+qFjobrP -lqnHpa9iPeheat7UV6BfeW3qmBIVl/s4IPE2ld4z0qqZiB0Tf6ssu/TpXNPsNXS6 -dLFz+myC+ufFdNEoQUtQitd5wKbjTCZCOGRaVRgJcSdG6Tq55Fa22mOKPm+mTmed -ZdKpAoGAFsTYBAHPxs8nzkCJCl7KLa4/zgbgywO6EcQgA7tfelB8bc8vcAMG5o+8 -YqAfwxrzhVSVbJx0fibTARXROmbh2pn010l2wj3+qUajM8NiskCPFbSjGy7HSUze -P8Kt1uMDJdj55gATzn44au31QBioZY2zXleorxF21cr+BZCJgfA= ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDlTCCAn2gAwIBAgICdxUwDQYJKoZIhvcNAQELBQAweTEbMBkGA1UEAxMSRHJp -dmVycyBUZXN0aW5nIENBMRAwDgYDVQQLEwdEcml2ZXJzMRAwDgYDVQQKEwdNb25n -b0RCMRYwFAYDVQQHEw1OZXcgWW9yayBDaXR5MREwDwYDVQQIEwhOZXcgWW9yazEL -MAkGA1UEBhMCVVMwHhcNMTkwNTIyMjIzMjU2WhcNMzkwNTIyMjIzMjU2WjBwMRIw -EAYDVQQDEwlsb2NhbGhvc3QxEDAOBgNVBAsTB0RyaXZlcnMxEDAOBgNVBAoTB01v -bmdvREIxFjAUBgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3Jr -MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAITa -wdBOhmP5BUnfP7zabv7fedrW5zrA+Xa1dFz75rsu0oCg2Gv0vsW/4z55WSinoXQZ -x+6j/4GG79+GcpM3AxWbHIzy4hnNnERj/jUAkhStHdv+3ctec0g984Y9eBvPjJKQ -Ho01PtP6hTWTHOZ3g7xmp03tPvGlmtA17LDNDLk8SL+4+grbDFnqiy6Hze2tSsNl -L5ongh3xzLgP9PKScEZ1f/uU/VjjQTyY2tmUEaxtQY6SOPNc0j1GXrCKovMg5RMX -b4DWkWSBB4v540RKsFYCTmaJlGOe85isqYjM28+PHQ07FYFErcaxKm2j/gRI0GD1 -+fkEJOBnTI5C3+di0ekCAwEAAaMwMC4wLAYDVR0RBCUwI4IJbG9jYWxob3N0hwR/ -AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQBol8+YH7MA -HwnIh7KcJ8h87GkCWsjOJCDJWiYBJArQ0MmgDO0qdx+QEtvLMn3XNtP05ZfK0WyX -or4cWllAkMFYaFbyB2hYazlD1UAAG+22Rku0UP6pJMLbWe6pnqzx+RL68FYdbZhN -fCW2xiiKsdPoo2VEY7eeZKrNr/0RFE5EKXgzmobpTBQT1Dl3Ve4aWLoTy9INlQ/g -z40qS7oq1PjjPLgxINhf4ncJqfmRXugYTOnyFiVXLZTys5Pb9SMKdToGl3NTYWLL -2AZdjr6bKtT+WtXyHqO0cQ8CkAW0M6VOlMluACllcJxfrtdlQS2S4lUIj76QKBdZ -khBHXq/b8MFX ------END CERTIFICATE----- diff --git a/mongo-orchestration/standalone/standalone-auth.json b/mongo-orchestration/standalone/standalone-auth.json deleted file mode 100644 index b15a68566..000000000 --- a/mongo-orchestration/standalone/standalone-auth.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "mongod", - "id" : "STANDALONE_AUTH", - "auth_key": "secret", - "login": "root", - "password": "toor", - "procParams": { - "dbpath": "/tmp/standalone-auth/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/standalone-auth/m.log", - "journal": true, - "port": 2200, - "bind_ip_all": true, - "setParameter": {"enableTestCommands": 1} - } -} diff --git a/mongo-orchestration/standalone/standalone-old.json b/mongo-orchestration/standalone/standalone-old.json deleted file mode 100644 index e89f013b8..000000000 --- a/mongo-orchestration/standalone/standalone-old.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "mongod", - "id" : "STANDALONE", - "procParams": { - "dbpath": "/tmp/standalone/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/standalone/mongod.log", - "journal": true, - "nssize": 1, - "port": 2700, - "bind_ip": "::,0.0.0.0", - "smallfiles": true, - "setParameter": {"enableTestCommands": 1} - } -} diff --git a/mongo-orchestration/standalone/standalone-ssl.json b/mongo-orchestration/standalone/standalone-ssl.json deleted file mode 100644 index f843589e6..000000000 --- a/mongo-orchestration/standalone/standalone-ssl.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "mongod", - "id" : "STANDALONE_SSL", - "procParams": { - "dbpath": "/tmp/standalone-ssl/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/standalone-ssl/m.log", - "journal": true, - "port": 2100, - "bind_ip_all": true, - "setParameter": {"enableTestCommands": 1} - }, - "sslParams": { - "sslMode": "requireSSL", - "sslCAFile": "$TRAVIS_BUILD_DIR/mongo-orchestration/ssl/ca.pem", - "sslPEMKeyFile": "$TRAVIS_BUILD_DIR/mongo-orchestration/ssl/server.pem", - "sslWeakCertificateValidation": true, - "sslAllowInvalidHostnames": true - } -} diff --git a/mongo-orchestration/standalone/standalone.json b/mongo-orchestration/standalone/standalone.json deleted file mode 100644 index 4cb451114..000000000 --- a/mongo-orchestration/standalone/standalone.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "mongod", - "id" : "STANDALONE", - "procParams": { - "dbpath": "/tmp/standalone/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/standalone/mongod.log", - "journal": true, - "port": 2000, - "bind_ip_all": true, - "setParameter": {"enableTestCommands": 1} - } -} diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 5fffede51..a4dee252f 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -1,53 +1,55 @@ - - - - + + + + - + - .phpcs/autoload.php - - .phpcs src + examples tests + tools + rector.php - - - - - - - - - - - - + + - + + + + + + + - - - + + + + + + + + + + - - - + + + - @@ -55,25 +57,29 @@ - + + + + - - + + + + - - - - - - + + + + + @@ -88,31 +94,90 @@ - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + tests + + + tests + + + tests + + + + + + + + src + + + src + + + + + + /src/GridFS/StreamWrapper /tests/DocumentationExamplesTest.php /tests/GridFS/UnusableStream.php + /tests/SpecTests/ClientSideEncryption/Prose* - + /examples /tests/PHPUnit/ConstraintTrait.php + + /examples + + + /tests/SpecTests/ClientSideEncryption/Prose* + diff --git a/phpunit.evergreen.xml b/phpunit.evergreen.xml index fae1bdf0e..56b35f6f8 100644 --- a/phpunit.evergreen.xml +++ b/phpunit.evergreen.xml @@ -11,7 +11,10 @@ > + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 13d1fdd41..c4f6cb2b7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -11,7 +11,9 @@ > + + diff --git a/psalm-baseline.xml b/psalm-baseline.xml new file mode 100644 index 000000000..778b248ae --- /dev/null +++ b/psalm-baseline.xml @@ -0,0 +1,803 @@ + + + + + $address + $emails + $id + $name + $type + + + + + self::CURSOR_NOT_FOUND + + + + + $driverOptions['driver'] ?? [] + + + $mergedDriver['platform'] + + + + + $cmd[$option] + $options['session'] + + + + + $cmd[$option] + $options['session'] + + + + + new static(sprintf('%s is immutable', $class)) + new static(sprintf('%s should not be called for an unacknowledged write result', $method)) + + + + + new static(sprintf('Expected %s to have type "%s" but found "%s"', $name, $expectedType, get_debug_type($value))) + + + + + new static('Resume token not found in change document') + new static(sprintf('Expected resume token to have type "array or object" but found "%s"', get_debug_type($value))) + + + + + new static('Array filters are not supported by the server executing this operation') + new static('Collations are not supported by the server executing this operation') + new static('Explain is not supported by the server executing this operation') + new static('Hint is not supported by the server executing this operation') + new static('Read concern is not supported by the server executing this command') + new static('The "allowDiskUse" option is not supported by the server executing this operation') + new static('The "commitQuorum" option is not supported by the server executing this operation') + new static('The "readConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.') + new static('The "writeConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.') + new static('Write concern is not supported by the server executing this command') + + + + + ! is_resource($destination) + ! is_resource($destination) + ! is_resource($source) + ! is_resource($stream) + + + delete + downloadToStream + downloadToStreamByName + drop + rename + + + $options['revision'] + $options['revision'] + + + + + new static(sprintf('Chunk not found for index "%d"', $expectedIndex)) + new static(sprintf('Expected chunk to have index "%d" but found "%d"', $expectedIndex, $index)) + new static(sprintf('Expected chunk to have size "%d" but found "%d"', $expectedSize, $size)) + new static(sprintf('Invalid data found for index "%d"', $chunkIndex)) + + + + + new static(sprintf('File "%s" not found in "%s"', $json, $namespace)) + new static(sprintf('File with name "%s" and revision "%d" not found in "%s"', $filename, $revision, $namespace)) + + + + + new static(sprintf('Downloading file from "%s" to "%s" failed. GridFS filename: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $filename)) + new static(sprintf('Downloading file from "%s" to "%s" failed. GridFS identifier: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $idString)) + new static(sprintf('Uploading file from "%s" to "%s" failed. GridFS filename: "%s"', $sourceMetadata['uri'], $destinationUri, $filename)) + + + + + $currentChunk->n + $this->file->length + + + + + $context[$this->protocol]['collectionWrapper'] + $context[$this->protocol]['collectionWrapper'] + $context[$this->protocol]['file'] + $context[$this->protocol]['filename'] + $context[$this->protocol]['options'] + + + $context[$this->protocol]['collectionWrapper'] + $context[$this->protocol]['collectionWrapper'] + $context[$this->protocol]['file'] + $context[$this->protocol]['filename'] + $context[$this->protocol]['options'] + + + stream_wrapper_unregister + + + + + hash_update + + + + + Traversable + + + call_user_func($this->getIterator) + + + + + $this[$key] + + + $key + $this[$key] + $value + + + new static() + + + + + $iteratorClass + + + $this[$key] + + + $key + $this[$key] + $value + + + new static() + + + + + $documentLength + $documentLength + $this->options['typeMap'] + + + $this->position + + + $documentLength + + + + + $currentItem[self::FIELD_KEY] + $currentItem[self::FIELD_VALUE] + + + $currentItem + $currentItem + + + + + ! is_array($document) && ! is_object($document) + isset($initialResumeToken) && ! is_array($initialResumeToken) && ! is_object($initialResumeToken) + + + $reply->cursor->nextBatch + $this->current() + + + $this->postBatchResumeToken + + + $reply->cursor->nextBatch + $reply->cursor->postBatchResumeToken + + + addSubscriber + removeSubscriber + + + + + $key + + + $this->info[$key] + + + + + $info + + + $info['idIndex']['ns'] + + + $info + + + $info['name'] + + + + + $key + + + $this->info[$key] + + + + + current($this->databases) + + + int + key($this->databases) + + + + + $key + + + $this->info[$key] + + + + + $info + $info + + + $info['ns'] + + + $info + + + + + $fieldName + + + $fieldName + $type + + + $type + + + $this->index['name'] + + + + + $this->options['typeMap'] + $this->options['typeMap'] + + + $cmdOptions['maxAwaitTimeMS'] + $cmd[$option] + $cmd['hint'] + $cmd['readConcern'] + $options[$option] + $options['writeConcern'] + + + isDefault + isDefault + isInTransaction + + + + + is_array($operation) + + + $args + $args + $args + $args[0] + $args[0] + $args[0] + $args[1] + $args[1] + $args[1] + $args[1] + $args[1] + $args[1] + $args[2] + + + $args[0] + $args[0] + $args[0] + $args[0] + $args[1] + $args[1] + $args[1] + $args[1] + $args[1] + $args[1] + $args[1] + $args[1] + $args[1] + $args[1] + $args[1] + $args[1] + $args[1] + $args[2] + $args[2] + $args[2] + $args[2] + $args[2] + $args[2] + $args[2] + $args[2]['upsert'] + $args[2]['upsert'] + $args[2]['upsert'] + $args[2]['upsert'] + + + $args[1] + $args[1] + $args[1] + $args[2] + $args[2] + $args[2] + $args[2] + $args[2] + $args[2] + $operations[$i][$type][1] + $operations[$i][$type][2] + $operations[$i][$type][2] + + + $args + $args + $args[2] + $args[2] + $insertedIds[$i] + $operations[$i][$type][1] + $operations[$i][$type][2] + $operations[$i][$type][2] + $options[$option] + $options['session'] + $options['writeConcern'] + + + isDefault + isInTransaction + + + $args[2] + $args[2] + + + + + ! is_array($filter) && ! is_object($filter) + + + $cmd[$option] + $cmd['hint'] + $cmd['readConcern'] + $options['readConcern'] + $options['readPreference'] + $options['session'] + + + isInTransaction + + + + + ! is_array($filter) && ! is_object($filter) + + + + + $options['pipeline'] + $this->options['typeMap'] + + + $cmd[$option] + $options['session'] + $options['writeConcern'] + + + + + $this->options['encryptedFields'] + + + + + is_array($index) + + + $cmd[$option] + $cmd['commitQuorum'] + $options['session'] + $options['writeConcern'] + + + isInTransaction + + + + + ! is_array($command) && ! is_object($command) + + + $this->options['typeMap'] + + + $options['readPreference'] + $options['session'] + + + + + ! is_array($filter) && ! is_object($filter) + + + $this->options['writeConcern'] + + + $cmd['comment'] + $deleteOptions['hint'] + $options['comment'] + $options['session'] + $options['writeConcern'] + + + isInTransaction + + + + + ! is_array($filter) && ! is_object($filter) + + + $this->options['typeMap'] + + + $cmd[$option] + $cmd['readConcern'] + $options['readConcern'] + $options['readPreference'] + $options['session'] + + + isInTransaction + + + + + $this->options['typeMap'] + + + $cmd['comment'] + $options['session'] + $options['writeConcern'] + + + isInTransaction + + + + + $this->options['typeMap'] + + + $cmd['comment'] + $options['session'] + $options['writeConcern'] + + + + + $this->options['typeMap'] + + + $cmd[$option] + $options['session'] + $options['writeConcern'] + + + isInTransaction + + + + + $this->options['typeMap'] + + + $cmd[$option] + $options['readPreference'] + $options['session'] + + + + + ! is_array($filter) && ! is_object($filter) + + + $this->options['typeMap'] + + + $options['modifiers'][$modifier[1]] + + + $options[$modifier[0]] + $options[$option] + $options['readPreference'] + $options['session'] + + + isInTransaction + + + + + $this->options['typeMap'] + $this->options['writeConcern'] + + + $cmd[$option] + $cmd['new'] + $cmd['upsert'] + $options['session'] + $options['writeConcern'] + + + array|object|null + + + isDefault + isInTransaction + + + is_object($result) ? ($result->value ?? null) : null + + + + + ! is_array($filter) && ! is_object($filter) + + + + + ! is_array($filter) && ! is_object($filter) + ! is_array($replacement) && ! is_object($replacement) + + + $options['returnDocument'] + + + + + ! is_array($filter) && ! is_object($filter) + ! is_array($update) && ! is_object($update) + + + $options['returnDocument'] + + + + + ! is_array($document) && ! is_object($document) + + + $insertedIds[$i] + $options[$option] + $options['session'] + $options['writeConcern'] + + + isDefault + isInTransaction + + + + + ! is_array($document) && ! is_object($document) + + + $insertedId + $options[$option] + $options['session'] + $options['writeConcern'] + + + isDefault + isInTransaction + + + + + function (array $collectionInfo) { + + + + + $cmd[$option] + $options['session'] + + + + + ! is_string($out) && ! is_array($out) && ! is_object($out) + + + $result->result->collection + $result->result->db + $this->options['typeMap'] + + + $cmd[$option] + $options['readConcern'] + $options['readPreference'] + $options['session'] + $options['writeConcern'] + + + getScope + isDefault + isDefault + isInTransaction + + + + + $this->options['typeMap'] + + + $cmd['comment'] + $options['session'] + $options['writeConcern'] + + + + + $this->options['typeMap'] + + + $cmd[$option] + $options['session'] + $options['writeConcern'] + + + isInTransaction + + + + + ! is_array($replacement) && ! is_object($replacement) + + + + + ! is_array($filter) && ! is_object($filter) + ! is_array($update) && ! is_object($update) + + + $this->options['writeConcern'] + + + $cmd['bypassDocumentValidation'] + $options[$option] + $options['session'] + $options['writeConcern'] + $updateOptions[$option] + + + isDefault + isInTransaction + + + + + ! is_array($update) && ! is_object($update) + + + + + ! is_array($update) && ! is_object($update) + + + + + isset($resumeToken) && ! is_array($resumeToken) && ! is_object($resumeToken) + + + $reply->cursor->firstBatch + + + $this->postBatchResumeToken + + + array|object|null + + + $reply->cursor->firstBatch + $reply->cursor->postBatchResumeToken + + + $this->changeStreamOptions['resumeAfter'] + $this->changeStreamOptions['startAfter'] + + + $firstBatchSize + $operationTime + + + $resumeToken === null && $this->operationTime !== null + $this->operationTime !== null + + + addSubscriber + removeSubscriber + + + + + $id + + + + + ! is_array($document) && ! is_object($document) + is_array($document) + + + $wireVersionForWriteStageOnSecondary + + + $manager->getServers() + + + $collectionInfo['options']['encryptedFields'] + + + $typeMap['fieldPaths'][$fieldPath] + $typeMap['fieldPaths'][substr($fieldPath, 0, -2)] + + + $element[$key] + $type + $typeMap['fieldPaths'][$fieldPath . '.' . $existingFieldPath] + $typeMap['fieldPaths'][$fieldPath] + $value + + + array|object|null + array|object|null + + + $collectionInfo['options']['encryptedFields'] ?? null + $encryptedFieldsMap[$databaseName . '.' . $collectionName] ?? null + + + diff --git a/psalm.xml.dist b/psalm.xml.dist new file mode 100644 index 000000000..5a922b4ca --- /dev/null +++ b/psalm.xml.dist @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/rector.php b/rector.php new file mode 100644 index 000000000..cf5d60690 --- /dev/null +++ b/rector.php @@ -0,0 +1,33 @@ +paths([ + __DIR__ . '/examples', + __DIR__ . '/src', + __DIR__ . '/tests', + __DIR__ . '/tools', + ]); + + // Modernize code + $rectorConfig->sets([LevelSetList::UP_TO_PHP_72]); + + $rectorConfig->skip([ + // Falsely detect unassigned variables in code paths stopped by PHPUnit\Framework\Assert::markTestSkipped() + AddDefaultValueForUndefinedVariableRector::class => [ + __DIR__ . '/tests/', + ], + // @see https://github.com/phpstan/phpstan-src/pull/2429 + RemoveExtraParametersRector::class => [ + __DIR__ . '/src/Operation/', + ], + ]); + + // All classes are public API by default, unless marked with @internal. + $rectorConfig->ruleWithConfiguration(RemoveAnnotationRector::class, ['api']); +}; diff --git a/src/BulkWriteResult.php b/src/BulkWriteResult.php index 68af2cd75..b73771e03 100644 --- a/src/BulkWriteResult.php +++ b/src/BulkWriteResult.php @@ -1,12 +1,12 @@ writeResult = $writeResult; @@ -51,8 +47,8 @@ public function __construct(WriteResult $writeResult, array $insertedIds) * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer - * @throws BadMethodCallException is the write result is unacknowledged + * @return integer|null + * @throws BadMethodCallException if the write result is unacknowledged */ public function getDeletedCount() { @@ -69,8 +65,8 @@ public function getDeletedCount() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer - * @throws BadMethodCallException is the write result is unacknowledged + * @return integer|null + * @throws BadMethodCallException if the write result is unacknowledged */ public function getInsertedCount() { @@ -90,7 +86,7 @@ public function getInsertedCount() * field value. Any driver-generated ID will be a MongoDB\BSON\ObjectId * instance. * - * @return mixed[] + * @return array */ public function getInsertedIds() { @@ -103,8 +99,8 @@ public function getInsertedIds() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer - * @throws BadMethodCallException is the write result is unacknowledged + * @return integer|null + * @throws BadMethodCallException if the write result is unacknowledged */ public function getMatchedCount() { @@ -125,7 +121,7 @@ public function getMatchedCount() * * @see BulkWriteResult::isAcknowledged() * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws BadMethodCallException if the write result is unacknowledged */ public function getModifiedCount() { @@ -142,8 +138,8 @@ public function getModifiedCount() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer - * @throws BadMethodCallException is the write result is unacknowledged + * @return integer|null + * @throws BadMethodCallException if the write result is unacknowledged */ public function getUpsertedCount() { @@ -165,8 +161,8 @@ public function getUpsertedCount() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return mixed[] - * @throws BadMethodCallException is the write result is unacknowledged + * @return array + * @throws BadMethodCallException if the write result is unacknowledged */ public function getUpsertedIds() { diff --git a/src/ChangeStream.php b/src/ChangeStream.php index fcf16dde8..91282e8fa 100644 --- a/src/ChangeStream.php +++ b/src/ChangeStream.php @@ -1,12 +1,12 @@ iterator->current(); } - /** - * @return CursorId - */ + /** @return CursorId */ public function getCursorId() { return $this->iterator->getInnerIterator()->getId(); @@ -129,9 +127,10 @@ public function getResumeToken() } /** - * @see http://php.net/iterator.key + * @see https://php.net/iterator.key * @return mixed */ + #[ReturnTypeWillChange] public function key() { if ($this->valid()) { @@ -142,10 +141,11 @@ public function key() } /** - * @see http://php.net/iterator.next + * @see https://php.net/iterator.next * @return void * @throws ResumeTokenException */ + #[ReturnTypeWillChange] public function next() { try { @@ -157,10 +157,11 @@ public function next() } /** - * @see http://php.net/iterator.rewind + * @see https://php.net/iterator.rewind * @return void * @throws ResumeTokenException */ + #[ReturnTypeWillChange] public function rewind() { try { @@ -175,9 +176,10 @@ public function rewind() } /** - * @see http://php.net/iterator.valid + * @see https://php.net/iterator.valid * @return boolean */ + #[ReturnTypeWillChange] public function valid() { return $this->iterator->valid(); @@ -187,10 +189,8 @@ public function valid() * Determines if an exception is a resumable error. * * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#resumable-error - * @param RuntimeException $exception - * @return boolean */ - private function isResumableError(RuntimeException $exception) + private function isResumableError(RuntimeException $exception): bool { if ($exception instanceof ConnectionException) { return true; @@ -200,15 +200,15 @@ private function isResumableError(RuntimeException $exception) return false; } - if ($exception->getCode() === self::$cursorNotFound) { + if ($exception->getCode() === self::CURSOR_NOT_FOUND) { return true; } - if (server_supports_feature($this->iterator->getServer(), self::$wireVersionForResumableChangeStreamError)) { + if (server_supports_feature($this->iterator->getServer(), self::WIRE_VERSION_FOR_RESUMABLE_CHANGE_STREAM_ERROR)) { return $exception->hasErrorLabel('ResumableChangeStreamError'); } - return in_array($exception->getCode(), self::$resumableErrorCodes); + return in_array($exception->getCode(), self::RESUMABLE_ERROR_CODES); } /** @@ -217,7 +217,7 @@ private function isResumableError(RuntimeException $exception) * @param boolean $incrementKey Increment $key if there is a current result * @throws ResumeTokenException */ - private function onIteration($incrementKey) + private function onIteration(bool $incrementKey): void { /* If the cursorId is 0, the server has invalidated the cursor and we * will never perform another getMore nor need to resume since any @@ -244,12 +244,15 @@ private function onIteration($incrementKey) /** * Recreates the ChangeStreamIterator after a resumable server error. - * - * @return void */ - private function resume() + private function resume(): void { + if (! $this->resumeCallable) { + throw new BadMethodCallException('Cannot resume a closed change stream.'); + } + $this->iterator = call_user_func($this->resumeCallable, $this->getResumeToken(), $this->hasAdvanced); + $this->iterator->rewind(); $this->onIteration($this->hasAdvanced); @@ -258,10 +261,9 @@ private function resume() /** * Either resumes after a resumable error or re-throws the exception. * - * @param RuntimeException $exception * @throws RuntimeException */ - private function resumeOrThrow(RuntimeException $exception) + private function resumeOrThrow(RuntimeException $exception): void { if ($this->isResumableError($exception)) { $this->resume(); diff --git a/src/Client.php b/src/Client.php index f3215d349..5b9eedea4 100644 --- a/src/Client.php +++ b/src/Client.php @@ -1,12 +1,12 @@ BSONArray::class, 'document' => BSONDocument::class, 'root' => BSONDocument::class, ]; - /** @var integer */ - private static $wireVersionForReadConcern = 4; - - /** @var integer */ - private static $wireVersionForWritableCommandWriteConcern = 5; - - /** @var string */ - private static $handshakeSeparator = ' / '; + private const HANDSHAKE_SEPARATOR = '/'; /** @var string|null */ private static $version; @@ -93,19 +88,19 @@ class Client * * Other options are documented in MongoDB\Driver\Manager::__construct(). * - * @see http://docs.mongodb.org/manual/reference/connection-string/ - * @see http://php.net/manual/en/mongodb-driver-manager.construct.php - * @see http://php.net/manual/en/mongodb.persistence.php#mongodb.persistence.typemaps - * @param string $uri MongoDB connection string - * @param array $uriOptions Additional connection string options - * @param array $driverOptions Driver-specific options + * @see https://mongodb.com/docs/manual/reference/connection-string/ + * @see https://php.net/manual/en/mongodb-driver-manager.construct.php + * @see https://php.net/manual/en/mongodb.persistence.php#mongodb.persistence.typemaps + * @param string|null $uri MongoDB connection string. If none is provided, this defaults to self::DEFAULT_URI. + * @param array $uriOptions Additional connection string options + * @param array $driverOptions Driver-specific options * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverInvalidArgumentException for parameter/option parsing errors in the driver * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function __construct($uri = 'mongodb://127.0.0.1/', array $uriOptions = [], array $driverOptions = []) + public function __construct(?string $uri = null, array $uriOptions = [], array $driverOptions = []) { - $driverOptions += ['typeMap' => self::$defaultTypeMap]; + $driverOptions += ['typeMap' => self::DEFAULT_TYPE_MAP]; if (! is_array($driverOptions['typeMap'])) { throw InvalidArgumentException::invalidType('"typeMap" driver option', $driverOptions['typeMap'], 'array'); @@ -121,8 +116,8 @@ public function __construct($uri = 'mongodb://127.0.0.1/', array $uriOptions = [ $driverOptions['driver'] = $this->mergeDriverInfo($driverOptions['driver'] ?? []); - $this->uri = (string) $uri; - $this->typeMap = $driverOptions['typeMap'] ?? null; + $this->uri = $uri ?? self::DEFAULT_URI; + $this->typeMap = $driverOptions['typeMap']; unset($driverOptions['typeMap']); @@ -135,7 +130,7 @@ public function __construct($uri = 'mongodb://127.0.0.1/', array $uriOptions = [ /** * Return internal properties for debugging purposes. * - * @see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo + * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo * @return array */ public function __debugInfo() @@ -155,12 +150,12 @@ public function __debugInfo() * be selected with complex syntax (e.g. $client->{"that-database"}) or * {@link selectDatabase()}. * - * @see http://php.net/oop5.overloading#object.get - * @see http://php.net/types.string#language.types.string.parsing.complex + * @see https://php.net/oop5.overloading#object.get + * @see https://php.net/types.string#language.types.string.parsing.complex * @param string $databaseName Name of the database to select * @return Database */ - public function __get($databaseName) + public function __get(string $databaseName) { return $this->selectDatabase($databaseName); } @@ -206,7 +201,7 @@ public function createClientEncryption(array $options) * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function dropDatabase($databaseName, array $options = []) + public function dropDatabase(string $databaseName, array $options = []) { if (! isset($options['typeMap'])) { $options['typeMap'] = $this->typeMap; @@ -214,7 +209,7 @@ public function dropDatabase($databaseName, array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -236,7 +231,7 @@ public function getManager() /** * Return the read concern for this client. * - * @see http://php.net/manual/en/mongodb-driver-readconcern.isdefault.php + * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php * @return ReadConcern */ public function getReadConcern() @@ -267,7 +262,7 @@ public function getTypeMap() /** * Return the write concern for this client. * - * @see http://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php + * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php * @return WriteConcern */ public function getWriteConcern() @@ -283,7 +278,7 @@ public function getWriteConcern() * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function listDatabaseNames(array $options = []) : Iterator + public function listDatabaseNames(array $options = []): Iterator { $operation = new ListDatabaseNames($options); $server = select_server($this->manager, $options); @@ -295,7 +290,6 @@ public function listDatabaseNames(array $options = []) : Iterator * List databases. * * @see ListDatabases::__construct() for supported options - * @param array $options * @return DatabaseInfoIterator * @throws UnexpectedValueException if the command response was malformed * @throws InvalidArgumentException for parameter/option parsing errors @@ -319,7 +313,7 @@ public function listDatabases(array $options = []) * @return Collection * @throws InvalidArgumentException for parameter/option parsing errors */ - public function selectCollection($databaseName, $collectionName, array $options = []) + public function selectCollection(string $databaseName, string $collectionName, array $options = []) { $options += ['typeMap' => $this->typeMap]; @@ -335,7 +329,7 @@ public function selectCollection($databaseName, $collectionName, array $options * @return Database * @throws InvalidArgumentException for parameter/option parsing errors */ - public function selectDatabase($databaseName, array $options = []) + public function selectDatabase(string $databaseName, array $options = []) { $options += ['typeMap' => $this->typeMap]; @@ -345,7 +339,7 @@ public function selectDatabase($databaseName, array $options = []) /** * Start a new client session. * - * @see http://php.net/manual/en/mongodb-driver-manager.startsession.php + * @see https://php.net/manual/en/mongodb-driver-manager.startsession.php * @param array $options Session options * @return Session */ @@ -358,7 +352,7 @@ public function startSession(array $options = []) * Create a change stream for watching changes to the cluster. * * @see Watch::__construct() for supported options - * @param array $pipeline List of pipeline operations + * @param array $pipeline Aggregation pipeline * @param array $options Command options * @return ChangeStream * @throws InvalidArgumentException for parameter/option parsing errors @@ -371,7 +365,7 @@ public function watch(array $pipeline = [], array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { + if (! isset($options['readConcern']) && ! is_in_transaction($options)) { $options['readConcern'] = $this->readConcern; } @@ -384,7 +378,7 @@ public function watch(array $pipeline = [], array $options = []) return $operation->execute($server); } - private static function getVersion() : string + private static function getVersion(): string { if (self::$version === null) { try { @@ -397,7 +391,7 @@ private static function getVersion() : string return self::$version; } - private function mergeDriverInfo(array $driver) : array + private function mergeDriverInfo(array $driver): array { $mergedDriver = [ 'name' => 'PHPLIB', @@ -409,7 +403,7 @@ private function mergeDriverInfo(array $driver) : array throw InvalidArgumentException::invalidType('"name" handshake option', $driver['name'], 'string'); } - $mergedDriver['name'] .= self::$handshakeSeparator . $driver['name']; + $mergedDriver['name'] .= self::HANDSHAKE_SEPARATOR . $driver['name']; } if (isset($driver['version'])) { @@ -417,7 +411,7 @@ private function mergeDriverInfo(array $driver) : array throw InvalidArgumentException::invalidType('"version" handshake option', $driver['version'], 'string'); } - $mergedDriver['version'] .= self::$handshakeSeparator . $driver['version']; + $mergedDriver['version'] .= self::HANDSHAKE_SEPARATOR . $driver['version']; } if (isset($driver['platform'])) { diff --git a/src/Collection.php b/src/Collection.php index a449c7edc..d92dc66da 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -1,12 +1,12 @@ BSONArray::class, 'document' => BSONDocument::class, 'root' => BSONDocument::class, ]; - /** @var integer */ - private static $wireVersionForFindAndModifyWriteConcern = 4; - - /** @var integer */ - private static $wireVersionForReadConcern = 4; - - /** @var integer */ - private static $wireVersionForWritableCommandWriteConcern = 5; - - /** @var integer */ - private static $wireVersionForReadConcernWithWriteStage = 8; + private const WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE = 8; /** @var string */ private $collectionName; @@ -133,7 +125,7 @@ class Collection * @param array $options Collection options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(Manager $manager, $databaseName, $collectionName, array $options = []) + public function __construct(Manager $manager, string $databaseName, string $collectionName, array $options = []) { if (strlen($databaseName) < 1) { throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName); @@ -160,18 +152,18 @@ public function __construct(Manager $manager, $databaseName, $collectionName, ar } $this->manager = $manager; - $this->databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern(); $this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference(); - $this->typeMap = $options['typeMap'] ?? self::$defaultTypeMap; + $this->typeMap = $options['typeMap'] ?? self::DEFAULT_TYPE_MAP; $this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern(); } /** * Return internal properties for debugging purposes. * - * @see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo + * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo * @return array */ public function __debugInfo() @@ -190,7 +182,7 @@ public function __debugInfo() /** * Return the collection namespace (e.g. "db.collection"). * - * @see https://docs.mongodb.org/manual/faq/developers/#faq-dev-namespace + * @see https://mongodb.com/docs/manual/core/databases-and-collections/ * @return string */ public function __toString() @@ -207,7 +199,7 @@ public function __toString() * "result" array from the command response document. * * @see Aggregate::__construct() for supported options - * @param array $pipeline List of pipeline operations + * @param array $pipeline Aggregation pipeline * @param array $options Command options * @return Traversable * @throws UnexpectedValueException if the command response was malformed @@ -223,21 +215,19 @@ public function aggregate(array $pipeline, array $options = []) $options['readPreference'] = $this->readPreference; } - if ($hasWriteStage) { - $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY); - } - - $server = select_server($this->manager, $options); + $server = $hasWriteStage + ? select_server_for_aggregate_write_stage($this->manager, $options) + : select_server($this->manager, $options); /* MongoDB 4.2 and later supports a read concern when an $out stage is * being used, but earlier versions do not. * * A read concern is also not compatible with transactions. */ - if (! isset($options['readConcern']) && - server_supports_feature($server, self::$wireVersionForReadConcern) && + if ( + ! isset($options['readConcern']) && ! is_in_transaction($options) && - ( ! $hasWriteStage || server_supports_feature($server, self::$wireVersionForReadConcernWithWriteStage)) + ( ! $hasWriteStage || server_supports_feature($server, self::WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE)) ) { $options['readConcern'] = $this->readConcern; } @@ -246,10 +236,7 @@ public function aggregate(array $pipeline, array $options = []) $options['typeMap'] = $this->typeMap; } - if ($hasWriteStage && - ! isset($options['writeConcern']) && - server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && - ! is_in_transaction($options)) { + if ($hasWriteStage && ! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -303,7 +290,7 @@ public function count($filter = [], array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { + if (! isset($options['readConcern']) && ! is_in_transaction($options)) { $options['readConcern'] = $this->readConcern; } @@ -332,7 +319,7 @@ public function countDocuments($filter = [], array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { + if (! isset($options['readConcern']) && ! is_in_transaction($options)) { $options['readConcern'] = $this->readConcern; } @@ -380,8 +367,8 @@ public function createIndex($key, array $options = []) * If the "name" option is unspecified, a name will be generated from the * "key" document. * - * @see http://docs.mongodb.org/manual/reference/command/createIndexes/ - * @see http://docs.mongodb.org/manual/reference/method/db.collection.createIndex/ + * @see https://mongodb.com/docs/manual/reference/command/createIndexes/ + * @see https://mongodb.com/docs/manual/reference/method/db.collection.createIndex/ * @see CreateIndexes::__construct() for supported command options * @param array[] $indexes List of index specifications * @param array $options Command options @@ -394,7 +381,7 @@ public function createIndexes(array $indexes, array $options = []) { $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -407,7 +394,7 @@ public function createIndexes(array $indexes, array $options = []) * Deletes all documents matching the filter. * * @see DeleteMany::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/delete/ + * @see https://mongodb.com/docs/manual/reference/command/delete/ * @param array|object $filter Query by which to delete documents * @param array $options Command options * @return DeleteResult @@ -431,7 +418,7 @@ public function deleteMany($filter, array $options = []) * Deletes at most one document matching the filter. * * @see DeleteOne::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/delete/ + * @see https://mongodb.com/docs/manual/reference/command/delete/ * @param array|object $filter Query by which to delete documents * @param array $options Command options * @return DeleteResult @@ -458,13 +445,13 @@ public function deleteOne($filter, array $options = []) * @param string $fieldName Field for which to return distinct values * @param array|object $filter Query by which to filter documents * @param array $options Command options - * @return mixed[] + * @return array * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function distinct($fieldName, $filter = [], array $options = []) + public function distinct(string $fieldName, $filter = [], array $options = []) { if (! isset($options['readPreference']) && ! is_in_transaction($options)) { $options['readPreference'] = $this->readPreference; @@ -476,7 +463,7 @@ public function distinct($fieldName, $filter = [], array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { + if (! isset($options['readConcern']) && ! is_in_transaction($options)) { $options['readConcern'] = $this->readConcern; } @@ -503,11 +490,18 @@ public function drop(array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } - $operation = new DropCollection($this->databaseName, $this->collectionName, $options); + if (! isset($options['encryptedFields'])) { + $options['encryptedFields'] = get_encrypted_fields_from_driver($this->databaseName, $this->collectionName, $this->manager) + ?? get_encrypted_fields_from_server($this->databaseName, $this->collectionName, $this->manager, $server); + } + + $operation = isset($options['encryptedFields']) + ? new DropEncryptedCollection($this->databaseName, $this->collectionName, $options) + : new DropCollection($this->databaseName, $this->collectionName, $options); return $operation->execute($server); } @@ -537,7 +531,7 @@ public function dropIndex($indexName, array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -564,7 +558,7 @@ public function dropIndexes(array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -592,7 +586,7 @@ public function estimatedDocumentCount(array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { + if (! isset($options['readConcern']) && ! is_in_transaction($options)) { $options['readConcern'] = $this->readConcern; } @@ -605,7 +599,7 @@ public function estimatedDocumentCount(array $options = []) * Explains explainable commands. * * @see Explain::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/explain/ + * @see https://mongodb.com/docs/manual/reference/command/explain/ * @param Explainable $explainable Command on which to run explain * @param array $options Additional options * @return array|object @@ -634,7 +628,7 @@ public function explain(Explainable $explainable, array $options = []) * Finds documents matching the query. * * @see Find::__construct() for supported options - * @see http://docs.mongodb.org/manual/core/read-operations-introduction/ + * @see https://mongodb.com/docs/manual/crud/#read-operations * @param array|object $filter Query by which to filter documents * @param array $options Additional options * @return Cursor @@ -650,7 +644,7 @@ public function find($filter = [], array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { + if (! isset($options['readConcern']) && ! is_in_transaction($options)) { $options['readConcern'] = $this->readConcern; } @@ -667,7 +661,7 @@ public function find($filter = [], array $options = []) * Finds a single document matching the query. * * @see FindOne::__construct() for supported options - * @see http://docs.mongodb.org/manual/core/read-operations-introduction/ + * @see https://mongodb.com/docs/manual/crud/#read-operations * @param array|object $filter Query by which to filter documents * @param array $options Additional options * @return array|object|null @@ -683,7 +677,7 @@ public function findOne($filter = [], array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { + if (! isset($options['readConcern']) && ! is_in_transaction($options)) { $options['readConcern'] = $this->readConcern; } @@ -702,7 +696,7 @@ public function findOne($filter = [], array $options = []) * The document to return may be null if no document matched the filter. * * @see FindOneAndDelete::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ * @param array|object $filter Query by which to filter documents * @param array $options Command options * @return array|object|null @@ -715,7 +709,7 @@ public function findOneAndDelete($filter, array $options = []) { $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForFindAndModifyWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -738,7 +732,7 @@ public function findOneAndDelete($filter, array $options = []) * to return the updated document. * * @see FindOneAndReplace::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ * @param array|object $filter Query by which to filter documents * @param array|object $replacement Replacement document * @param array $options Command options @@ -752,7 +746,7 @@ public function findOneAndReplace($filter, $replacement, array $options = []) { $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForFindAndModifyWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -775,7 +769,7 @@ public function findOneAndReplace($filter, $replacement, array $options = []) * to return the updated document. * * @see FindOneAndReplace::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched document * @param array $options Command options @@ -789,7 +783,7 @@ public function findOneAndUpdate($filter, $update, array $options = []) { $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForFindAndModifyWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -835,7 +829,7 @@ public function getManager() /** * Return the collection namespace. * - * @see https://docs.mongodb.org/manual/reference/glossary/#term-namespace + * @see https://mongodb.com/docs/manual/reference/glossary/#term-namespace * @return string */ public function getNamespace() @@ -846,7 +840,7 @@ public function getNamespace() /** * Return the read concern for this collection. * - * @see http://php.net/manual/en/mongodb-driver-readconcern.isdefault.php + * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php * @return ReadConcern */ public function getReadConcern() @@ -877,7 +871,7 @@ public function getTypeMap() /** * Return the write concern for this collection. * - * @see http://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php + * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php * @return WriteConcern */ public function getWriteConcern() @@ -889,7 +883,7 @@ public function getWriteConcern() * Inserts multiple documents. * * @see InsertMany::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/insert/ + * @see https://mongodb.com/docs/manual/reference/command/insert/ * @param array[]|object[] $documents The documents to insert * @param array $options Command options * @return InsertManyResult @@ -912,7 +906,7 @@ public function insertMany(array $documents, array $options = []) * Inserts one document. * * @see InsertOne::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/insert/ + * @see https://mongodb.com/docs/manual/reference/command/insert/ * @param array|object $document The document to insert * @param array $options Command options * @return InsertOneResult @@ -935,7 +929,6 @@ public function insertOne($document, array $options = []) * Returns information for all indexes for the collection. * * @see ListIndexes::__construct() for supported options - * @param array $options * @return IndexInfoIterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -952,7 +945,7 @@ public function listIndexes(array $options = []) * Executes a map-reduce aggregation on the collection. * * @see MapReduce::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/mapReduce/ + * @see https://mongodb.com/docs/manual/reference/command/mapReduce/ * @param JavascriptInterface $map Map function * @param JavascriptInterface $reduce Reduce function * @param string|array|object $out Output specification @@ -973,7 +966,7 @@ public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, // Check if the out option is inline because we will want to coerce a primary read preference if not if ($hasOutputCollection) { - $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY); + $options['readPreference'] = new ReadPreference(ReadPreference::PRIMARY); } $server = select_server($this->manager, $options); @@ -983,7 +976,7 @@ public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, * * A read concern is also not compatible with transactions. */ - if (! isset($options['readConcern']) && ! ($hasOutputCollection && $this->readConcern->getLevel() === ReadConcern::MAJORITY) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { + if (! isset($options['readConcern']) && ! ($hasOutputCollection && $this->readConcern->getLevel() === ReadConcern::MAJORITY) && ! is_in_transaction($options)) { $options['readConcern'] = $this->readConcern; } @@ -991,7 +984,7 @@ public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, $options['typeMap'] = $this->typeMap; } - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -1000,11 +993,44 @@ public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, return $operation->execute($server); } + /** + * Renames the collection. + * + * @see RenameCollection::__construct() for supported options + * @param string $toCollectionName New name of the collection + * @param string|null $toDatabaseName New database name of the collection. Defaults to the original database. + * @param array $options Additional options + * @return array|object Command result document + * @throws UnsupportedException if options are not supported by the selected server + * @throws InvalidArgumentException for parameter/option parsing errors + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function rename(string $toCollectionName, ?string $toDatabaseName = null, array $options = []) + { + if (! isset($toDatabaseName)) { + $toDatabaseName = $this->databaseName; + } + + if (! isset($options['typeMap'])) { + $options['typeMap'] = $this->typeMap; + } + + $server = select_server($this->manager, $options); + + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { + $options['writeConcern'] = $this->writeConcern; + } + + $operation = new RenameCollection($this->databaseName, $this->collectionName, $toDatabaseName, $toCollectionName, $options); + + return $operation->execute($server); + } + /** * Replaces at most one document matching the filter. * * @see ReplaceOne::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/update/ + * @see https://mongodb.com/docs/manual/reference/command/update/ * @param array|object $filter Query by which to filter documents * @param array|object $replacement Replacement document * @param array $options Command options @@ -1029,7 +1055,7 @@ public function replaceOne($filter, $replacement, array $options = []) * Updates all documents matching the filter. * * @see UpdateMany::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/update/ + * @see https://mongodb.com/docs/manual/reference/command/update/ * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched documents * @param array $options Command options @@ -1054,7 +1080,7 @@ public function updateMany($filter, $update, array $options = []) * Updates at most one document matching the filter. * * @see UpdateOne::__construct() for supported options - * @see http://docs.mongodb.org/manual/reference/command/update/ + * @see https://mongodb.com/docs/manual/reference/command/update/ * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched document * @param array $options Command options @@ -1079,7 +1105,7 @@ public function updateOne($filter, $update, array $options = []) * Create a change stream for watching changes to the collection. * * @see Watch::__construct() for supported options - * @param array $pipeline List of pipeline operations + * @param array $pipeline Aggregation pipeline * @param array $options Command options * @return ChangeStream * @throws InvalidArgumentException for parameter/option parsing errors @@ -1099,7 +1125,7 @@ public function watch(array $pipeline = [], array $options = []) * related to change streams being unsupported instead of an * UnsupportedException regarding use of the "readConcern" option from * the Aggregate operation class. */ - if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { + if (! isset($options['readConcern']) && ! is_in_transaction($options)) { $options['readConcern'] = $this->readConcern; } diff --git a/src/Command/ListCollections.php b/src/Command/ListCollections.php index 4f85f1371..a970aeb07 100644 --- a/src/Command/ListCollections.php +++ b/src/Command/ListCollections.php @@ -6,7 +6,7 @@ * 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 + * https://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, @@ -24,6 +24,7 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Model\CachingIterator; use MongoDB\Operation\Executable; + use function is_array; use function is_bool; use function is_integer; @@ -33,7 +34,7 @@ * Wrapper for the listCollections command. * * @internal - * @see http://docs.mongodb.org/manual/reference/command/listCollections/ + * @see https://mongodb.com/docs/manual/reference/command/listCollections/ */ class ListCollections implements Executable { @@ -48,6 +49,15 @@ class ListCollections implements Executable * * Supported options: * + * * authorizedCollections (boolean): Determines which collections are + * returned based on the user privileges. + * + * For servers < 4.0, this option is ignored. + * + * * comment (mixed): BSON value to attach as a comment to this command. + * + * This is not supported for servers versions < 4.4. + * * * filter (document): Query by which to filter collections. * * * maxTimeMS (integer): The maximum amount of time to allow the query to @@ -59,14 +69,16 @@ class ListCollections implements Executable * * * session (MongoDB\Driver\Session): Client session. * - * Sessions are not supported for server versions < 3.6. - * * @param string $databaseName Database name * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct($databaseName, array $options = []) + public function __construct(string $databaseName, array $options = []) { + if (isset($options['authorizedCollections']) && ! is_bool($options['authorizedCollections'])) { + throw InvalidArgumentException::invalidType('"authorizedCollections" option', $options['authorizedCollections'], 'boolean'); + } + if (isset($options['filter']) && ! is_array($options['filter']) && ! is_object($options['filter'])) { throw InvalidArgumentException::invalidType('"filter" option', $options['filter'], 'array or object'); } @@ -83,7 +95,7 @@ public function __construct($databaseName, array $options = []) throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); } - $this->databaseName = (string) $databaseName; + $this->databaseName = $databaseName; $this->options = $options; } @@ -91,11 +103,20 @@ public function __construct($databaseName, array $options = []) * Execute the operation. * * @see Executable::execute() - * @param Server $server - * @return CachingIterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): CachingIterator + { + $cursor = $server->executeReadCommand($this->databaseName, $this->createCommand(), $this->createOptions()); + $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); + + return new CachingIterator($cursor); + } + + /** + * Create the listCollections command. + */ + private function createCommand(): Command { $cmd = ['listCollections' => 1]; @@ -103,18 +124,13 @@ public function execute(Server $server) $cmd['filter'] = (object) $this->options['filter']; } - if (isset($this->options['maxTimeMS'])) { - $cmd['maxTimeMS'] = $this->options['maxTimeMS']; + foreach (['authorizedCollections', 'comment', 'maxTimeMS', 'nameOnly'] as $option) { + if (isset($this->options[$option])) { + $cmd[$option] = $this->options[$option]; + } } - if (isset($this->options['nameOnly'])) { - $cmd['nameOnly'] = $this->options['nameOnly']; - } - - $cursor = $server->executeReadCommand($this->databaseName, new Command($cmd), $this->createOptions()); - $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); - - return new CachingIterator($cursor); + return new Command($cmd); } /** @@ -123,10 +139,9 @@ public function execute(Server $server) * Note: read preference is intentionally omitted, as the spec requires that * the command be executed on the primary. * - * @see http://php.net/manual/en/mongodb-driver-server.executecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; diff --git a/src/Command/ListDatabases.php b/src/Command/ListDatabases.php index c9fa58fc8..4dabc6ed2 100644 --- a/src/Command/ListDatabases.php +++ b/src/Command/ListDatabases.php @@ -6,7 +6,7 @@ * 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 + * https://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, @@ -24,6 +24,7 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; use MongoDB\Operation\Executable; + use function current; use function is_array; use function is_bool; @@ -34,7 +35,7 @@ * Wrapper for the ListDatabases command. * * @internal - * @see http://docs.mongodb.org/manual/reference/command/listDatabases/ + * @see https://mongodb.com/docs/manual/reference/command/listDatabases/ */ class ListDatabases implements Executable { @@ -51,9 +52,11 @@ class ListDatabases implements Executable * * For servers < 4.0.5, this option is ignored. * - * * filter (document): Query by which to filter databases. + * * comment (mixed): BSON value to attach as a comment to this command. * - * For servers < 3.6, this option is ignored. + * This is not supported for servers versions < 4.4. + * + * * filter (document): Query by which to filter databases. * * * maxTimeMS (integer): The maximum amount of time to allow the query to * run. @@ -64,8 +67,6 @@ class ListDatabases implements Executable * * * session (MongoDB\Driver\Session): Client session. * - * Sessions are not supported for server versions < 3.6. - * * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ @@ -98,40 +99,41 @@ public function __construct(array $options = []) * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return array An array of database info structures * @throws UnexpectedValueException if the command response was malformed * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array { - $cmd = ['listDatabases' => 1]; + $cursor = $server->executeReadCommand('admin', $this->createCommand(), $this->createOptions()); + $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); + $result = current($cursor->toArray()); - if (isset($this->options['authorizedDatabases'])) { - $cmd['authorizedDatabases'] = $this->options['authorizedDatabases']; + if (! isset($result['databases']) || ! is_array($result['databases'])) { + throw new UnexpectedValueException('listDatabases command did not return a "databases" array'); } - if (! empty($this->options['filter'])) { - $cmd['filter'] = (object) $this->options['filter']; - } + return $result['databases']; + } - if (isset($this->options['maxTimeMS'])) { - $cmd['maxTimeMS'] = $this->options['maxTimeMS']; - } + /** + * Create the listDatabases command. + */ + private function createCommand(): Command + { + $cmd = ['listDatabases' => 1]; - if (isset($this->options['nameOnly'])) { - $cmd['nameOnly'] = $this->options['nameOnly']; + if (! empty($this->options['filter'])) { + $cmd['filter'] = (object) $this->options['filter']; } - $cursor = $server->executeReadCommand('admin', new Command($cmd), $this->createOptions()); - $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); - $result = current($cursor->toArray()); - - if (! isset($result['databases']) || ! is_array($result['databases'])) { - throw new UnexpectedValueException('listDatabases command did not return a "databases" array'); + foreach (['authorizedDatabases', 'comment', 'maxTimeMS', 'nameOnly'] as $option) { + if (isset($this->options[$option])) { + $cmd[$option] = $this->options[$option]; + } } - return $result['databases']; + return new Command($cmd); } /** @@ -140,10 +142,9 @@ public function execute(Server $server) * Note: read preference is intentionally omitted, as the spec requires that * the command be executed on the primary. * - * @see http://php.net/manual/en/mongodb-driver-server.executecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; diff --git a/src/Database.php b/src/Database.php index fadc64be5..f2f9278b5 100644 --- a/src/Database.php +++ b/src/Database.php @@ -1,12 +1,12 @@ BSONArray::class, 'document' => BSONDocument::class, 'root' => BSONDocument::class, ]; - /** @var integer */ - private static $wireVersionForReadConcern = 4; - - /** @var integer */ - private static $wireVersionForWritableCommandWriteConcern = 5; - - /** @var integer */ - private static $wireVersionForReadConcernWithWriteStage = 8; + private const WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE = 8; /** @var string */ private $databaseName; @@ -107,7 +106,7 @@ class Database * @param array $options Database options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(Manager $manager, $databaseName, array $options = []) + public function __construct(Manager $manager, string $databaseName, array $options = []) { if (strlen($databaseName) < 1) { throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName); @@ -130,17 +129,17 @@ public function __construct(Manager $manager, $databaseName, array $options = [] } $this->manager = $manager; - $this->databaseName = (string) $databaseName; + $this->databaseName = $databaseName; $this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern(); $this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference(); - $this->typeMap = $options['typeMap'] ?? self::$defaultTypeMap; + $this->typeMap = $options['typeMap'] ?? self::DEFAULT_TYPE_MAP; $this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern(); } /** * Return internal properties for debugging purposes. * - * @see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo + * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo * @return array */ public function __debugInfo() @@ -162,12 +161,12 @@ public function __debugInfo() * be selected with complex syntax (e.g. $database->{"system.profile"}) or * {@link selectCollection()}. * - * @see http://php.net/oop5.overloading#object.get - * @see http://php.net/types.string#language.types.string.parsing.complex + * @see https://php.net/oop5.overloading#object.get + * @see https://php.net/types.string#language.types.string.parsing.complex * @param string $collectionName Name of the collection to select * @return Collection */ - public function __get($collectionName) + public function __get(string $collectionName) { return $this->selectCollection($collectionName); } @@ -188,7 +187,7 @@ public function __toString() * and $listLocalSessions. Requires MongoDB >= 3.6 * * @see Aggregate::__construct() for supported options - * @param array $pipeline List of pipeline operations + * @param array $pipeline Aggregation pipeline * @param array $options Command options * @return Traversable * @throws UnexpectedValueException if the command response was malformed @@ -204,21 +203,19 @@ public function aggregate(array $pipeline, array $options = []) $options['readPreference'] = $this->readPreference; } - if ($hasWriteStage) { - $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY); - } - - $server = select_server($this->manager, $options); + $server = $hasWriteStage + ? select_server_for_aggregate_write_stage($this->manager, $options) + : select_server($this->manager, $options); /* MongoDB 4.2 and later supports a read concern when an $out stage is * being used, but earlier versions do not. * * A read concern is also not compatible with transactions. */ - if (! isset($options['readConcern']) && - server_supports_feature($server, self::$wireVersionForReadConcern) && + if ( + ! isset($options['readConcern']) && ! is_in_transaction($options) && - ( ! $hasWriteStage || server_supports_feature($server, self::$wireVersionForReadConcernWithWriteStage)) + ( ! $hasWriteStage || server_supports_feature($server, self::WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE)) ) { $options['readConcern'] = $this->readConcern; } @@ -227,10 +224,7 @@ public function aggregate(array $pipeline, array $options = []) $options['typeMap'] = $this->typeMap; } - if ($hasWriteStage && - ! isset($options['writeConcern']) && - server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && - ! is_in_transaction($options)) { + if ($hasWriteStage && ! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -264,29 +258,82 @@ public function command($command, array $options = []) /** * Create a new collection explicitly. * + * If the "encryptedFields" option is specified, this method additionally + * creates related metadata collections and an index on the encrypted + * collection. + * * @see CreateCollection::__construct() for supported options - * @param string $collectionName - * @param array $options + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#create-collection-helper + * @see https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/manage-collections/ * @return array|object Command result document * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function createCollection($collectionName, array $options = []) + public function createCollection(string $collectionName, array $options = []) { if (! isset($options['typeMap'])) { $options['typeMap'] = $this->typeMap; } + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { + $options['writeConcern'] = $this->writeConcern; + } + + if (! isset($options['encryptedFields'])) { + $options['encryptedFields'] = get_encrypted_fields_from_driver($this->databaseName, $collectionName, $this->manager); + } + + $operation = isset($options['encryptedFields']) + ? new CreateEncryptedCollection($this->databaseName, $collectionName, $options) + : new CreateCollection($this->databaseName, $collectionName, $options); + $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { + return $operation->execute($server); + } + + /** + * Create a new encrypted collection explicitly. + * + * The "encryptedFields" option is required. + * + * This method will automatically create data keys for any encrypted fields + * where "keyId" is null. A copy of the modified "encryptedFields" option + * will be returned in addition to the result from creating the collection. + * + * If any error is encountered creating data keys or the collection, a + * CreateEncryptedCollectionException will be thrown. The original exception + * and modified "encryptedFields" option can be accessed via the + * getPrevious() and getEncryptedFields() methods, respectively. + * + * @see CreateCollection::__construct() for supported options + * @return array A tuple containing the command result document from creating the collection and the modified "encryptedFields" option + * @throws InvalidArgumentException for parameter/option parsing errors + * @throws CreateEncryptedCollectionException for any errors creating data keys or creating the collection + * @throws UnsupportedException if Queryable Encryption is not supported by the selected server + */ + public function createEncryptedCollection(string $collectionName, ClientEncryption $clientEncryption, string $kmsProvider, ?array $masterKey, array $options): array + { + if (! isset($options['typeMap'])) { + $options['typeMap'] = $this->typeMap; + } + + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } - $operation = new CreateCollection($this->databaseName, $collectionName, $options); + $operation = new CreateEncryptedCollection($this->databaseName, $collectionName, $options); + $server = select_server($this->manager, $options); - return $operation->execute($server); + try { + $operation->createDataKeys($clientEncryption, $kmsProvider, $masterKey, $encryptedFields); + $result = $operation->execute($server); + + return [$result, $encryptedFields]; + } catch (Throwable $e) { + throw new CreateEncryptedCollectionException($e, $encryptedFields ?? []); + } } /** @@ -307,7 +354,7 @@ public function drop(array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -327,7 +374,7 @@ public function drop(array $options = []) * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function dropCollection($collectionName, array $options = []) + public function dropCollection(string $collectionName, array $options = []) { if (! isset($options['typeMap'])) { $options['typeMap'] = $this->typeMap; @@ -335,11 +382,18 @@ public function dropCollection($collectionName, array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } - $operation = new DropCollection($this->databaseName, $collectionName, $options); + if (! isset($options['encryptedFields'])) { + $options['encryptedFields'] = get_encrypted_fields_from_driver($this->databaseName, $collectionName, $this->manager) + ?? get_encrypted_fields_from_server($this->databaseName, $collectionName, $this->manager, $server); + } + + $operation = isset($options['encryptedFields']) + ? new DropEncryptedCollection($this->databaseName, $collectionName, $options) + : new DropCollection($this->databaseName, $collectionName, $options); return $operation->execute($server); } @@ -367,7 +421,7 @@ public function getManager() /** * Return the read concern for this database. * - * @see http://php.net/manual/en/mongodb-driver-readconcern.isdefault.php + * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php * @return ReadConcern */ public function getReadConcern() @@ -398,7 +452,7 @@ public function getTypeMap() /** * Return the write concern for this database. * - * @see http://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php + * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php * @return WriteConcern */ public function getWriteConcern() @@ -413,7 +467,7 @@ public function getWriteConcern() * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function listCollectionNames(array $options = []) : Iterator + public function listCollectionNames(array $options = []): Iterator { $operation = new ListCollectionNames($this->databaseName, $options); $server = select_server($this->manager, $options); @@ -425,7 +479,6 @@ public function listCollectionNames(array $options = []) : Iterator * Returns information for all collections in this database. * * @see ListCollections::__construct() for supported options - * @param array $options * @return CollectionInfoIterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -449,7 +502,7 @@ public function listCollections(array $options = []) * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function modifyCollection($collectionName, array $collectionOptions, array $options = []) + public function modifyCollection(string $collectionName, array $collectionOptions, array $options = []) { if (! isset($options['typeMap'])) { $options['typeMap'] = $this->typeMap; @@ -457,7 +510,7 @@ public function modifyCollection($collectionName, array $collectionOptions, arra $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -466,6 +519,40 @@ public function modifyCollection($collectionName, array $collectionOptions, arra return $operation->execute($server); } + /** + * Rename a collection within this database. + * + * @see RenameCollection::__construct() for supported options + * @param string $fromCollectionName Collection name + * @param string $toCollectionName New name of the collection + * @param string|null $toDatabaseName New database name of the collection. Defaults to the original database. + * @param array $options Additional options + * @return array|object Command result document + * @throws UnsupportedException if options are unsupported on the selected server + * @throws InvalidArgumentException for parameter/option parsing errors + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function renameCollection(string $fromCollectionName, string $toCollectionName, ?string $toDatabaseName = null, array $options = []) + { + if (! isset($toDatabaseName)) { + $toDatabaseName = $this->databaseName; + } + + if (! isset($options['typeMap'])) { + $options['typeMap'] = $this->typeMap; + } + + $server = select_server($this->manager, $options); + + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { + $options['writeConcern'] = $this->writeConcern; + } + + $operation = new RenameCollection($this->databaseName, $fromCollectionName, $toDatabaseName, $toCollectionName, $options); + + return $operation->execute($server); + } + /** * Select a collection within this database. * @@ -475,7 +562,7 @@ public function modifyCollection($collectionName, array $collectionOptions, arra * @return Collection * @throws InvalidArgumentException for parameter/option parsing errors */ - public function selectCollection($collectionName, array $options = []) + public function selectCollection(string $collectionName, array $options = []) { $options += [ 'readConcern' => $this->readConcern, @@ -511,7 +598,7 @@ public function selectGridFSBucket(array $options = []) * Create a change stream for watching changes to the database. * * @see Watch::__construct() for supported options - * @param array $pipeline List of pipeline operations + * @param array $pipeline Aggregation pipeline * @param array $options Command options * @return ChangeStream * @throws InvalidArgumentException for parameter/option parsing errors @@ -524,7 +611,7 @@ public function watch(array $pipeline = [], array $options = []) $server = select_server($this->manager, $options); - if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { + if (! isset($options['readConcern']) && ! is_in_transaction($options)) { $options['readConcern'] = $this->readConcern; } diff --git a/src/DeleteResult.php b/src/DeleteResult.php index c5b9ce64d..3c5b2cdd4 100644 --- a/src/DeleteResult.php +++ b/src/DeleteResult.php @@ -1,12 +1,12 @@ getMessage()), 0, $previous); + + $this->encryptedFields = $encryptedFields; + } + + /** + * Returns the encryptedFields option in its last known state before the + * operation was interrupted. + * + * This can be used to infer which data keys were successfully created; + * however, it is possible that additional data keys were successfully + * created and are not present in the returned value. For example, if the + * operation was interrupted by a timeout error when creating a data key, + * the write may actually have succeeded on the server but the key will not + * be present in the returned value. + */ + public function getEncryptedFields(): array + { + return $this->encryptedFields; + } +} diff --git a/src/Exception/Exception.php b/src/Exception/Exception.php index 154a19391..7bb3e0d3f 100644 --- a/src/Exception/Exception.php +++ b/src/Exception/Exception.php @@ -1,12 +1,12 @@ $expectedType Expected type as a string or an array containing one or more strings * @return self */ - public static function invalidType($name, $value, $expectedType) + public static function invalidType(string $name, $value, $expectedType) { if (is_array($expectedType)) { - switch (count($expectedType)) { - case 1: - $typeString = array_pop($expectedType); - break; - - case 2: - $typeString = implode('" or "', $expectedType); - break; - - default: - $lastType = array_pop($expectedType); - $typeString = sprintf('%s", or "%s', implode('", "', $expectedType), $lastType); - break; - } - - $expectedType = $typeString; + $expectedType = self::expectedTypesToString($expectedType); } return new static(sprintf('Expected %s to have type "%s" but found "%s"', $name, $expectedType, get_debug_type($value))); } + + /** @param list $types */ + private static function expectedTypesToString(array $types): string + { + assert(count($types) > 0); + + if (count($types) < 3) { + return implode('" or "', $types); + } + + $lastType = array_pop($types); + + return sprintf('%s", or "%s', implode('", "', $types), $lastType); + } } diff --git a/src/Exception/ResumeTokenException.php b/src/Exception/ResumeTokenException.php index 362e72fa8..4d3b23188 100644 --- a/src/Exception/ResumeTokenException.php +++ b/src/Exception/ResumeTokenException.php @@ -1,12 +1,12 @@ BSONArray::class, 'document' => BSONDocument::class, 'root' => BSONDocument::class, ]; - /** @var string */ - private static $streamWrapperProtocol = 'gridfs'; + private const STREAM_WRAPPER_PROTOCOL = 'gridfs'; /** @var CollectionWrapper */ private $collectionWrapper; @@ -136,11 +131,11 @@ class Bucket * @param array $options Bucket options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(Manager $manager, $databaseName, array $options = []) + public function __construct(Manager $manager, string $databaseName, array $options = []) { $options += [ - 'bucketName' => self::$defaultBucketName, - 'chunkSizeBytes' => self::$defaultChunkSizeBytes, + 'bucketName' => self::DEFAULT_BUCKET_NAME, + 'chunkSizeBytes' => self::DEFAULT_CHUNK_SIZE_BYTES, 'disableMD5' => false, ]; @@ -177,13 +172,13 @@ public function __construct(Manager $manager, $databaseName, array $options = [] } $this->manager = $manager; - $this->databaseName = (string) $databaseName; + $this->databaseName = $databaseName; $this->bucketName = $options['bucketName']; $this->chunkSizeBytes = $options['chunkSizeBytes']; $this->disableMD5 = $options['disableMD5']; $this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern(); $this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference(); - $this->typeMap = $options['typeMap'] ?? self::$defaultTypeMap; + $this->typeMap = $options['typeMap'] ?? self::DEFAULT_TYPE_MAP; $this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern(); $collectionOptions = array_intersect_key($options, ['readConcern' => 1, 'readPreference' => 1, 'typeMap' => 1, 'writeConcern' => 1]); @@ -195,7 +190,7 @@ public function __construct(Manager $manager, $databaseName, array $options = [] /** * Return internal properties for debugging purposes. * - * @see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo + * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo * @return array */ public function __debugInfo() @@ -203,6 +198,7 @@ public function __debugInfo() return [ 'bucketName' => $this->bucketName, 'databaseName' => $this->databaseName, + 'disableMD5' => $this->disableMD5, 'manager' => $this->manager, 'chunkSizeBytes' => $this->chunkSizeBytes, 'readConcern' => $this->readConcern, @@ -281,7 +277,7 @@ public function downloadToStream($id, $destination) * @throws StreamException if the file could not be uploaded * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function downloadToStreamByName($filename, $destination, array $options = []) + public function downloadToStreamByName(string $filename, $destination, array $options = []) { if (! is_resource($destination) || get_resource_type($destination) != "stream") { throw InvalidArgumentException::invalidType('$destination', $destination, 'resource'); @@ -412,6 +408,7 @@ public function getFileIdForStream($stream) */ $typeMap = ['root' => 'stdClass'] + $this->typeMap; $file = apply_type_map_to_document($file, $typeMap); + assert(is_object($file)); if (! isset($file->_id) && ! property_exists($file, '_id')) { throw new CorruptFileException('file._id does not exist'); @@ -433,7 +430,7 @@ public function getFilesCollection() /** * Return the read concern for this GridFS bucket. * - * @see http://php.net/manual/en/mongodb-driver-readconcern.isdefault.php + * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php * @return ReadConcern */ public function getReadConcern() @@ -464,7 +461,7 @@ public function getTypeMap() /** * Return the write concern for this GridFS bucket. * - * @see http://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php + * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php * @return WriteConcern */ public function getWriteConcern() @@ -492,7 +489,7 @@ public function openDownloadStream($id) } /** - * Opens a readable stream stream to read a GridFS file, which is selected + * Opens a readable stream to read a GridFS file, which is selected * by name and revision. * * Supported options: @@ -516,7 +513,7 @@ public function openDownloadStream($id) * @throws FileNotFoundException if no file could be selected * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function openDownloadStreamByName($filename, array $options = []) + public function openDownloadStreamByName(string $filename, array $options = []) { $options += ['revision' => -1]; @@ -549,13 +546,16 @@ public function openDownloadStreamByName($filename, array $options = []) * @param array $options Upload options * @return resource */ - public function openUploadStream($filename, array $options = []) + public function openUploadStream(string $filename, array $options = []) { - $options += ['chunkSizeBytes' => $this->chunkSizeBytes]; + $options += [ + 'chunkSizeBytes' => $this->chunkSizeBytes, + 'disableMD5' => $this->disableMD5, + ]; $path = $this->createPathForUpload(); $context = stream_context_create([ - self::$streamWrapperProtocol => [ + self::STREAM_WRAPPER_PROTOCOL => [ 'collectionWrapper' => $this->collectionWrapper, 'filename' => $filename, 'options' => $options, @@ -573,7 +573,7 @@ public function openUploadStream($filename, array $options = []) * @throws FileNotFoundException if no file could be selected * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function rename($id, $newFilename) + public function rename($id, string $newFilename) { $updateResult = $this->collectionWrapper->updateFilenameForId($id, $newFilename); @@ -619,7 +619,7 @@ public function rename($id, $newFilename) * @throws StreamException if the file could not be uploaded * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function uploadFromStream($filename, $source, array $options = []) + public function uploadFromStream(string $filename, $source, array $options = []) { if (! is_resource($source) || get_resource_type($source) != "stream") { throw InvalidArgumentException::invalidType('$source', $source, 'resource'); @@ -629,6 +629,7 @@ public function uploadFromStream($filename, $source, array $options = []) if (@stream_copy_to_stream($source, $destination) === false) { $destinationUri = $this->createPathForFile($this->getRawFileDocumentForStream($destination)); + throw StreamException::uploadFailed($filename, $source, $destinationUri); } @@ -638,20 +639,19 @@ public function uploadFromStream($filename, $source, array $options = []) /** * Creates a path for an existing GridFS file. * - * @param stdClass $file GridFS file document - * @return string + * @param object $file GridFS file document */ - private function createPathForFile(stdClass $file) + private function createPathForFile(object $file): string { - if (! is_object($file->_id) || method_exists($file->_id, '__toString')) { - $id = (string) $file->_id; - } else { + if (is_array($file->_id) || (is_object($file->_id) && ! method_exists($file->_id, '__toString'))) { $id = toJSON(fromPHP(['_id' => $file->_id])); + } else { + $id = (string) $file->_id; } return sprintf( '%s://%s/%s.files/%s', - self::$streamWrapperProtocol, + self::STREAM_WRAPPER_PROTOCOL, urlencode($this->databaseName), urlencode($this->bucketName), urlencode($id) @@ -660,14 +660,12 @@ private function createPathForFile(stdClass $file) /** * Creates a path for a new GridFS file, which does not yet have an ID. - * - * @return string */ - private function createPathForUpload() + private function createPathForUpload(): string { return sprintf( '%s://%s/%s.files', - self::$streamWrapperProtocol, + self::STREAM_WRAPPER_PROTOCOL, urlencode($this->databaseName), urlencode($this->bucketName) ); @@ -675,10 +673,8 @@ private function createPathForUpload() /** * Returns the names of the files collection. - * - * @return string */ - private function getFilesNamespace() + private function getFilesNamespace(): string { return sprintf('%s.%s.files', $this->databaseName, $this->bucketName); } @@ -690,10 +686,9 @@ private function getFilesNamespace() * respect the Bucket's type map. * * @param resource $stream GridFS stream - * @return stdClass * @throws InvalidArgumentException */ - private function getRawFileDocumentForStream($stream) + private function getRawFileDocumentForStream($stream): object { if (! is_resource($stream) || get_resource_type($stream) != "stream") { throw InvalidArgumentException::invalidType('$stream', $stream, 'resource'); @@ -711,14 +706,14 @@ private function getRawFileDocumentForStream($stream) /** * Opens a readable stream for the GridFS file. * - * @param stdClass $file GridFS file document + * @param object $file GridFS file document * @return resource */ - private function openDownloadStreamByFile(stdClass $file) + private function openDownloadStreamByFile(object $file) { $path = $this->createPathForFile($file); $context = stream_context_create([ - self::$streamWrapperProtocol => [ + self::STREAM_WRAPPER_PROTOCOL => [ 'collectionWrapper' => $this->collectionWrapper, 'file' => $file, ], @@ -730,12 +725,12 @@ private function openDownloadStreamByFile(stdClass $file) /** * Registers the GridFS stream wrapper if it is not already registered. */ - private function registerStreamWrapper() + private function registerStreamWrapper(): void { - if (in_array(self::$streamWrapperProtocol, stream_get_wrappers())) { + if (in_array(self::STREAM_WRAPPER_PROTOCOL, stream_get_wrappers())) { return; } - StreamWrapper::register(self::$streamWrapperProtocol); + StreamWrapper::register(self::STREAM_WRAPPER_PROTOCOL); } } diff --git a/src/GridFS/CollectionWrapper.php b/src/GridFS/CollectionWrapper.php index 0b7f43091..4642028e3 100644 --- a/src/GridFS/CollectionWrapper.php +++ b/src/GridFS/CollectionWrapper.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; - $this->bucketName = (string) $bucketName; + $this->databaseName = $databaseName; + $this->bucketName = $bucketName; $this->filesCollection = new Collection($manager, $databaseName, sprintf('%s.files', $bucketName), $collectionOptions); $this->chunksCollection = new Collection($manager, $databaseName, sprintf('%s.chunks', $bucketName), $collectionOptions); @@ -77,7 +79,7 @@ public function __construct(Manager $manager, $databaseName, $bucketName, array * * @param mixed $id */ - public function deleteChunksByFilesId($id) + public function deleteChunksByFilesId($id): void { $this->chunksCollection->deleteMany(['files_id' => $id]); } @@ -87,7 +89,7 @@ public function deleteChunksByFilesId($id) * * @param mixed $id */ - public function deleteFileAndChunksById($id) + public function deleteFileAndChunksById($id): void { $this->filesCollection->deleteOne(['_id' => $id]); $this->chunksCollection->deleteMany(['files_id' => $id]); @@ -96,7 +98,7 @@ public function deleteFileAndChunksById($id) /** * Drops the GridFS files and chunks collections. */ - public function dropCollections() + public function dropCollections(): void { $this->filesCollection->drop(['typeMap' => []]); $this->chunksCollection->drop(['typeMap' => []]); @@ -107,9 +109,8 @@ public function dropCollections() * * @param mixed $id File ID * @param integer $fromChunk Starting chunk (inclusive) - * @return Cursor */ - public function findChunksByFileId($id, $fromChunk = 0) + public function findChunksByFileId($id, int $fromChunk = 0): Cursor { return $this->chunksCollection->find( [ @@ -137,14 +138,11 @@ public function findChunksByFileId($id, $fromChunk = 0) * * @see Bucket::downloadToStreamByName() * @see Bucket::openDownloadStreamByName() - * @param string $filename - * @param integer $revision - * @return stdClass|null */ - public function findFileByFilenameAndRevision($filename, $revision) + public function findFileByFilenameAndRevision(string $filename, int $revision): ?object { - $filename = (string) $filename; - $revision = (integer) $revision; + $filename = $filename; + $revision = $revision; if ($revision < 0) { $skip = abs($revision) - 1; @@ -154,7 +152,7 @@ public function findFileByFilenameAndRevision($filename, $revision) $sortOrder = 1; } - return $this->filesCollection->findOne( + $file = $this->filesCollection->findOne( ['filename' => $filename], [ 'skip' => $skip, @@ -162,20 +160,25 @@ public function findFileByFilenameAndRevision($filename, $revision) 'typeMap' => ['root' => 'stdClass'], ] ); + assert(is_object($file) || $file === null); + + return $file; } /** * Finds a GridFS file document for a given ID. * * @param mixed $id - * @return stdClass|null */ - public function findFileById($id) + public function findFileById($id): ?object { - return $this->filesCollection->findOne( + $file = $this->filesCollection->findOne( ['_id' => $id], ['typeMap' => ['root' => 'stdClass']] ); + assert(is_object($file) || $file === null); + + return $file; } /** @@ -203,42 +206,22 @@ public function findOneFile($filter, array $options = []) return $this->filesCollection->findOne($filter, $options); } - /** - * Return the bucket name. - * - * @return string - */ - public function getBucketName() + public function getBucketName(): string { return $this->bucketName; } - /** - * Return the chunks collection. - * - * @return Collection - */ - public function getChunksCollection() + public function getChunksCollection(): Collection { return $this->chunksCollection; } - /** - * Return the database name. - * - * @return string - */ - public function getDatabaseName() + public function getDatabaseName(): string { return $this->databaseName; } - /** - * Return the files collection. - * - * @return Collection - */ - public function getFilesCollection() + public function getFilesCollection(): Collection { return $this->filesCollection; } @@ -248,7 +231,7 @@ public function getFilesCollection() * * @param array|object $chunk Chunk document */ - public function insertChunk($chunk) + public function insertChunk($chunk): void { if (! $this->checkedIndexes) { $this->ensureIndexes(); @@ -264,7 +247,7 @@ public function insertChunk($chunk) * * @param array|object $file File document */ - public function insertFile($file) + public function insertFile($file): void { if (! $this->checkedIndexes) { $this->ensureIndexes(); @@ -276,22 +259,20 @@ public function insertFile($file) /** * Updates the filename field in the file document for a given ID. * - * @param mixed $id - * @param string $filename - * @return UpdateResult + * @param mixed $id */ - public function updateFilenameForId($id, $filename) + public function updateFilenameForId($id, string $filename): UpdateResult { return $this->filesCollection->updateOne( ['_id' => $id], - ['$set' => ['filename' => (string) $filename]] + ['$set' => ['filename' => $filename]] ); } /** * Create an index on the chunks collection if it does not already exist. */ - private function ensureChunksIndex() + private function ensureChunksIndex(): void { $expectedIndex = ['files_id' => 1, 'n' => 1]; @@ -307,7 +288,7 @@ private function ensureChunksIndex() /** * Create an index on the files collection if it does not already exist. */ - private function ensureFilesIndex() + private function ensureFilesIndex(): void { $expectedIndex = ['filename' => 1, 'uploadDate' => 1]; @@ -326,7 +307,7 @@ private function ensureFilesIndex() * This method is called once before the first write operation on a GridFS * bucket. Indexes are only be created if the files collection is empty. */ - private function ensureIndexes() + private function ensureIndexes(): void { if ($this->checkedIndexes) { return; @@ -342,7 +323,7 @@ private function ensureIndexes() $this->ensureChunksIndex(); } - private function indexKeysMatch(array $expectedKeys, array $actualKeys) : bool + private function indexKeysMatch(array $expectedKeys, array $actualKeys): bool { if (count($expectedKeys) !== count($actualKeys)) { return false; @@ -353,8 +334,8 @@ private function indexKeysMatch(array $expectedKeys, array $actualKeys) : bool $iterator->attachIterator(new ArrayIterator($actualKeys)); foreach ($iterator as $key => $value) { - list($expectedKey, $actualKey) = $key; - list($expectedValue, $actualValue) = $value; + [$expectedKey, $actualKey] = $key; + [$expectedValue, $actualValue] = $value; if ($expectedKey !== $actualKey) { return false; @@ -374,13 +355,11 @@ private function indexKeysMatch(array $expectedKeys, array $actualKeys) : bool /** * Returns whether the files collection is empty. - * - * @return boolean */ - private function isFilesCollectionEmpty() + private function isFilesCollectionEmpty(): bool { return null === $this->filesCollection->findOne([], [ - 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), 'projection' => ['_id' => 1], 'typeMap' => [], ]); diff --git a/src/GridFS/Exception/CorruptFileException.php b/src/GridFS/Exception/CorruptFileException.php index c35372569..e31d7daeb 100644 --- a/src/GridFS/Exception/CorruptFileException.php +++ b/src/GridFS/Exception/CorruptFileException.php @@ -1,12 +1,12 @@ $id])); diff --git a/src/GridFS/Exception/StreamException.php b/src/GridFS/Exception/StreamException.php index 53aa9e351..241f1b9a4 100644 --- a/src/GridFS/Exception/StreamException.php +++ b/src/GridFS/Exception/StreamException.php @@ -3,6 +3,7 @@ namespace MongoDB\GridFS\Exception; use MongoDB\Exception\RuntimeException; + use function MongoDB\BSON\fromPHP; use function MongoDB\BSON\toJSON; use function sprintf; @@ -14,7 +15,7 @@ class StreamException extends RuntimeException * @param resource $source * @param resource $destination */ - public static function downloadFromFilenameFailed(string $filename, $source, $destination) : self + public static function downloadFromFilenameFailed(string $filename, $source, $destination): self { $sourceMetadata = stream_get_meta_data($source); $destinationMetadata = stream_get_meta_data($destination); @@ -27,7 +28,7 @@ public static function downloadFromFilenameFailed(string $filename, $source, $de * @param resource $source * @param resource $destination */ - public static function downloadFromIdFailed($id, $source, $destination) : self + public static function downloadFromIdFailed($id, $source, $destination): self { $idString = toJSON(fromPHP(['_id' => $id])); $sourceMetadata = stream_get_meta_data($source); @@ -37,7 +38,7 @@ public static function downloadFromIdFailed($id, $source, $destination) : self } /** @param resource $source */ - public static function uploadFailed(string $filename, $source, string $destinationUri) : self + public static function uploadFailed(string $filename, $source, string $destinationUri): self { $sourceMetadata = stream_get_meta_data($source); diff --git a/src/GridFS/ReadableStream.php b/src/GridFS/ReadableStream.php index 7978de972..7c3d8be4b 100644 --- a/src/GridFS/ReadableStream.php +++ b/src/GridFS/ReadableStream.php @@ -1,12 +1,12 @@ chunkSize) || ! is_integer($file->chunkSize) || $file->chunkSize < 1) { throw new CorruptFileException('file.chunkSize is not an integer >= 1'); @@ -88,24 +91,24 @@ public function __construct(CollectionWrapper $collectionWrapper, stdClass $file } $this->file = $file; - $this->chunkSize = (integer) $file->chunkSize; - $this->length = (integer) $file->length; + $this->chunkSize = $file->chunkSize; + $this->length = $file->length; $this->collectionWrapper = $collectionWrapper; if ($this->length > 0) { $this->numChunks = (integer) ceil($this->length / $this->chunkSize); - $this->expectedLastChunkSize = ($this->length - (($this->numChunks - 1) * $this->chunkSize)); + $this->expectedLastChunkSize = $this->length - (($this->numChunks - 1) * $this->chunkSize); } } /** * Return internal properties for debugging purposes. * - * @see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo + * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return [ 'bucketName' => $this->collectionWrapper->getBucketName(), @@ -114,37 +117,25 @@ public function __debugInfo() ]; } - public function close() + public function close(): void { // Nothing to do } - /** - * Return the stream's file document. - * - * @return stdClass - */ - public function getFile() + public function getFile(): object { return $this->file; } - /** - * Return the stream's size in bytes. - * - * @return integer - */ - public function getSize() + public function getSize(): int { return $this->length; } /** * Return whether the current read position is at the end of the stream. - * - * @return boolean */ - public function isEOF() + public function isEOF(): bool { if ($this->chunkOffset === $this->numChunks - 1) { return $this->bufferOffset >= $this->expectedLastChunkSize; @@ -160,10 +151,9 @@ public function isEOF() * if data is not available to be read. * * @param integer $length Number of bytes to read - * @return string * @throws InvalidArgumentException if $length is negative */ - public function readBytes($length) + public function readBytes(int $length): string { if ($length < 0) { throw new InvalidArgumentException(sprintf('$length must be >= 0; given: %d', $length)); @@ -177,6 +167,8 @@ public function readBytes($length) return ''; } + assert($this->buffer !== null); + $data = ''; while (strlen($data) < $length) { @@ -195,10 +187,9 @@ public function readBytes($length) /** * Seeks the chunk and buffer offsets for the next read operation. * - * @param integer $offset * @throws InvalidArgumentException if $offset is out of range */ - public function seek($offset) + public function seek(int $offset): void { if ($offset < 0 || $offset > $this->file->length) { throw new InvalidArgumentException(sprintf('$offset must be >= 0 and <= %d; given: %d', $this->file->length, $offset)); @@ -233,7 +224,7 @@ public function seek($offset) } /* If we are seeking to a subsequent chunk, we do not need to - * reinitalize the chunk iterator. Instead, we can simply move forward + * reinitalize the chunk iterator. Instead, we can move forward * to $this->chunkOffset. */ $numChunks = $this->chunkOffset - $lastChunkOffset; @@ -246,10 +237,8 @@ public function seek($offset) * Return the current position of the stream. * * This is the offset within the stream where the next byte would be read. - * - * @return integer */ - public function tell() + public function tell(): int { return ($this->chunkOffset * $this->chunkSize) + $this->bufferOffset; } @@ -260,22 +249,31 @@ public function tell() * @return boolean Whether there was a current chunk to read * @throws CorruptFileException if an expected chunk could not be read successfully */ - private function initBufferFromCurrentChunk() + private function initBufferFromCurrentChunk(): bool { if ($this->chunkOffset === 0 && $this->numChunks === 0) { return false; } + if ($this->chunksIterator === null) { + return false; + } + if (! $this->chunksIterator->valid()) { throw CorruptFileException::missingChunk($this->chunkOffset); } $currentChunk = $this->chunksIterator->current(); + assert(is_object($currentChunk)); if ($currentChunk->n !== $this->chunkOffset) { throw CorruptFileException::unexpectedIndex($currentChunk->n, $this->chunkOffset); } + if (! $currentChunk->data instanceof Binary) { + throw CorruptFileException::invalidChunkData($this->chunkOffset); + } + $this->buffer = $currentChunk->data->getData(); $actualChunkSize = strlen($this->buffer); @@ -297,12 +295,16 @@ private function initBufferFromCurrentChunk() * @return boolean Whether there was a next chunk to read * @throws CorruptFileException if an expected chunk could not be read successfully */ - private function initBufferFromNextChunk() + private function initBufferFromNextChunk(): bool { if ($this->chunkOffset === $this->numChunks - 1) { return false; } + if ($this->chunksIterator === null) { + return false; + } + $this->bufferOffset = 0; $this->chunkOffset++; $this->chunksIterator->next(); @@ -313,11 +315,9 @@ private function initBufferFromNextChunk() /** * Initializes the chunk iterator starting from the current offset. */ - private function initChunksIterator() + private function initChunksIterator(): void { - $cursor = $this->collectionWrapper->findChunksByFileId($this->file->_id, $this->chunkOffset); - - $this->chunksIterator = new IteratorIterator($cursor); + $this->chunksIterator = $this->collectionWrapper->findChunksByFileId($this->file->_id, $this->chunkOffset); $this->chunksIterator->rewind(); } } diff --git a/src/GridFS/StreamWrapper.php b/src/GridFS/StreamWrapper.php index a5136b349..a90104e9a 100644 --- a/src/GridFS/StreamWrapper.php +++ b/src/GridFS/StreamWrapper.php @@ -1,12 +1,12 @@ stream !== null); + return $this->stream->getFile(); } @@ -80,7 +75,7 @@ public function getFile() * * @param string $protocol Protocol to use for stream_wrapper_register() */ - public static function register($protocol = 'gridfs') + public static function register(string $protocol = 'gridfs'): void { if (in_array($protocol, stream_get_wrappers())) { stream_wrapper_unregister($protocol); @@ -92,9 +87,9 @@ public static function register($protocol = 'gridfs') /** * Closes the stream. * - * @see http://php.net/manual/en/streamwrapper.stream-close.php + * @see https://php.net/manual/en/streamwrapper.stream-close.php */ - public function stream_close() + public function stream_close(): void { if (! $this->stream) { return; @@ -106,10 +101,9 @@ public function stream_close() /** * Returns whether the file pointer is at the end of the stream. * - * @see http://php.net/manual/en/streamwrapper.stream-eof.php - * @return boolean + * @see https://php.net/manual/en/streamwrapper.stream-eof.php */ - public function stream_eof() + public function stream_eof(): bool { if (! $this->stream instanceof ReadableStream) { return false; @@ -121,17 +115,15 @@ public function stream_eof() /** * Opens the stream. * - * @see http://php.net/manual/en/streamwrapper.stream-open.php - * @param string $path Path to the file resource - * @param string $mode Mode used to open the file (only "r" and "w" are supported) - * @param integer $options Additional flags set by the streams API - * @param string $openedPath Not used - * @return boolean + * @see https://php.net/manual/en/streamwrapper.stream-open.php + * @param string $path Path to the file resource + * @param string $mode Mode used to open the file (only "r" and "w" are supported) + * @param integer $options Additional flags set by the streams API + * @param string|null $openedPath Not used */ - public function stream_open($path, $mode, $options, &$openedPath) + public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool { $this->initProtocol($path); - $this->mode = $mode; if ($mode === 'r') { return $this->initReadableStream(); @@ -150,35 +142,30 @@ public function stream_open($path, $mode, $options, &$openedPath) * Note: this method may return a string smaller than the requested length * if data is not available to be read. * - * @see http://php.net/manual/en/streamwrapper.stream-read.php + * @see https://php.net/manual/en/streamwrapper.stream-read.php * @param integer $length Number of bytes to read - * @return string */ - public function stream_read($length) + public function stream_read(int $length): string { if (! $this->stream instanceof ReadableStream) { return ''; } - try { - return $this->stream->readBytes($length); - } catch (Throwable $e) { - trigger_error(sprintf('%s: %s', get_class($e), $e->getMessage()), E_USER_WARNING); - - return false; - } + return $this->stream->readBytes($length); } /** * Return the current position of the stream. * - * @see http://php.net/manual/en/streamwrapper.stream-seek.php + * @see https://php.net/manual/en/streamwrapper.stream-seek.php * @param integer $offset Stream offset to seek to * @param integer $whence One of SEEK_SET, SEEK_CUR, or SEEK_END * @return boolean True if the position was updated and false otherwise */ - public function stream_seek($offset, $whence = SEEK_SET) + public function stream_seek(int $offset, int $whence = SEEK_SET): bool { + assert($this->stream !== null); + $size = $this->stream->getSize(); if ($whence === SEEK_CUR) { @@ -206,11 +193,12 @@ public function stream_seek($offset, $whence = SEEK_SET) /** * Return information about the stream. * - * @see http://php.net/manual/en/streamwrapper.stream-stat.php - * @return array + * @see https://php.net/manual/en/streamwrapper.stream-stat.php */ - public function stream_stat() + public function stream_stat(): array { + assert($this->stream !== null); + $stat = $this->getStatTemplate(); $stat[2] = $stat['mode'] = $this->stream instanceof ReadableStream @@ -236,42 +224,36 @@ public function stream_stat() /** * Return the current position of the stream. * - * @see http://php.net/manual/en/streamwrapper.stream-tell.php + * @see https://php.net/manual/en/streamwrapper.stream-tell.php * @return integer The current position of the stream */ - public function stream_tell() + public function stream_tell(): int { + assert($this->stream !== null); + return $this->stream->tell(); } /** * Write bytes to the stream. * - * @see http://php.net/manual/en/streamwrapper.stream-write.php + * @see https://php.net/manual/en/streamwrapper.stream-write.php * @param string $data Data to write * @return integer The number of bytes written */ - public function stream_write($data) + public function stream_write(string $data): int { if (! $this->stream instanceof WritableStream) { return 0; } - try { - return $this->stream->writeBytes($data); - } catch (Throwable $e) { - trigger_error(sprintf('%s: %s', get_class($e), $e->getMessage()), E_USER_WARNING); - - return false; - } + return $this->stream->writeBytes($data); } /** * Returns a stat template with default values. - * - * @return array */ - private function getStatTemplate() + private function getStatTemplate(): array { return [ // phpcs:disable Squiz.Arrays.ArrayDeclaration.IndexNoNewline @@ -296,9 +278,8 @@ private function getStatTemplate() * Initialize the protocol from the given path. * * @see StreamWrapper::stream_open() - * @param string $path */ - private function initProtocol($path) + private function initProtocol(string $path): void { $parts = explode('://', $path, 2); $this->protocol = $parts[0] ?: 'gridfs'; @@ -308,12 +289,13 @@ private function initProtocol($path) * Initialize the internal stream for reading. * * @see StreamWrapper::stream_open() - * @return boolean */ - private function initReadableStream() + private function initReadableStream(): bool { + assert(is_resource($this->context)); $context = stream_context_get_options($this->context); + assert($this->protocol !== null); $this->stream = new ReadableStream( $context[$this->protocol]['collectionWrapper'], $context[$this->protocol]['file'] @@ -326,12 +308,13 @@ private function initReadableStream() * Initialize the internal stream for writing. * * @see StreamWrapper::stream_open() - * @return boolean */ - private function initWritableStream() + private function initWritableStream(): bool { + assert(is_resource($this->context)); $context = stream_context_get_options($this->context); + assert($this->protocol !== null); $this->stream = new WritableStream( $context[$this->protocol]['collectionWrapper'], $context[$this->protocol]['filename'], diff --git a/src/GridFS/WritableStream.php b/src/GridFS/WritableStream.php index 2e12285dd..2228244d5 100644 --- a/src/GridFS/WritableStream.php +++ b/src/GridFS/WritableStream.php @@ -1,12 +1,12 @@ new ObjectId(), - 'chunkSizeBytes' => self::$defaultChunkSizeBytes, + 'chunkSizeBytes' => self::DEFAULT_CHUNK_SIZE_BYTES, 'disableMD5' => false, ]; @@ -145,17 +145,16 @@ public function __construct(CollectionWrapper $collectionWrapper, $filename, arr $this->file = [ '_id' => $options['_id'], 'chunkSize' => $this->chunkSize, - 'filename' => (string) $filename, + 'filename' => $filename, ] + array_intersect_key($options, ['aliases' => 1, 'contentType' => 1, 'metadata' => 1]); } /** * Return internal properties for debugging purposes. * - * @see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - * @return array + * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo */ - public function __debugInfo() + public function __debugInfo(): array { return [ 'bucketName' => $this->collectionWrapper->getBucketName(), @@ -167,7 +166,7 @@ public function __debugInfo() /** * Closes an active stream and flushes all buffered data to GridFS. */ - public function close() + public function close(): void { if ($this->isClosed) { // TODO: Should this be an error condition? e.g. BadMethodCallException @@ -184,10 +183,8 @@ public function close() /** * Return the stream's file document. - * - * @return stdClass */ - public function getFile() + public function getFile(): object { return (object) $this->file; } @@ -196,10 +193,8 @@ public function getFile() * Return the stream's size in bytes. * * Note: this value will increase as more data is written to the stream. - * - * @return integer */ - public function getSize() + public function getSize(): int { return $this->length + strlen($this->buffer); } @@ -212,9 +207,8 @@ public function getSize() * always the end of the stream. * * @see WritableStream::getSize() - * @return integer */ - public function tell() + public function tell(): int { return $this->getSize(); } @@ -226,13 +220,12 @@ public function tell() * which point a chunk document will be inserted and the buffer reset. * * @param string $data Binary data to write - * @return integer */ - public function writeBytes($data) + public function writeBytes(string $data): int { if ($this->isClosed) { // TODO: Should this be an error condition? e.g. BadMethodCallException - return; + return 0; } $bytesRead = 0; @@ -250,7 +243,7 @@ public function writeBytes($data) return $bytesRead; } - private function abort() + private function abort(): void { try { $this->collectionWrapper->deleteChunksByFilesId($this->file['_id']); @@ -261,12 +254,12 @@ private function abort() $this->isClosed = true; } - private function fileCollectionInsert() + private function fileCollectionInsert(): void { $this->file['length'] = $this->length; $this->file['uploadDate'] = new UTCDateTime(); - if (! $this->disableMD5) { + if (! $this->disableMD5 && $this->hashCtx) { $this->file['md5'] = hash_final($this->hashCtx); } @@ -277,11 +270,9 @@ private function fileCollectionInsert() throw $e; } - - return $this->file['_id']; } - private function insertChunkFromBuffer() + private function insertChunkFromBuffer(): void { if (strlen($this->buffer) == 0) { return; @@ -296,7 +287,7 @@ private function insertChunkFromBuffer() 'data' => new Binary($data, Binary::TYPE_GENERIC), ]; - if (! $this->disableMD5) { + if (! $this->disableMD5 && $this->hashCtx) { hash_update($this->hashCtx, $data); } diff --git a/src/InsertManyResult.php b/src/InsertManyResult.php index 2d288af4c..5de89920e 100644 --- a/src/InsertManyResult.php +++ b/src/InsertManyResult.php @@ -1,12 +1,12 @@ writeResult = $writeResult; @@ -51,8 +47,8 @@ public function __construct(WriteResult $writeResult, array $insertedIds) * This method should only be called if the write was acknowledged. * * @see InsertManyResult::isAcknowledged() - * @return integer - * @throws BadMethodCallException is the write result is unacknowledged + * @return integer|null + * @throws BadMethodCallException if the write result is unacknowledged */ public function getInsertedCount() { @@ -72,7 +68,7 @@ public function getInsertedCount() * field value. Any driver-generated ID will be a MongoDB\BSON\ObjectId * instance. * - * @return mixed[] + * @return array */ public function getInsertedIds() { diff --git a/src/InsertOneResult.php b/src/InsertOneResult.php index c4dee6e93..0a387579f 100644 --- a/src/InsertOneResult.php +++ b/src/InsertOneResult.php @@ -1,12 +1,12 @@ writeResult = $writeResult; @@ -51,8 +48,8 @@ public function __construct(WriteResult $writeResult, $insertedId) * This method should only be called if the write was acknowledged. * * @see InsertOneResult::isAcknowledged() - * @return integer - * @throws BadMethodCallException is the write result is unacknowledged + * @return integer|null + * @throws BadMethodCallException if the write result is unacknowledged */ public function getInsertedCount() { diff --git a/src/MapReduceResult.php b/src/MapReduceResult.php index aaa36b172..4c70b4a07 100644 --- a/src/MapReduceResult.php +++ b/src/MapReduceResult.php @@ -1,12 +1,12 @@ executionTimeMS; + return $this->executionTimeMS; } /** * Return the mapReduce results as a Traversable. * - * @see http://php.net/iteratoraggregate.getiterator + * @see https://php.net/iteratoraggregate.getiterator * @return Traversable */ + #[ReturnTypeWillChange] public function getIterator() { return call_user_func($this->getIterator); diff --git a/src/Model/BSONArray.php b/src/Model/BSONArray.php index 8230a5e5a..c10d05faf 100644 --- a/src/Model/BSONArray.php +++ b/src/Model/BSONArray.php @@ -6,7 +6,7 @@ * 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 + * https://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, @@ -21,6 +21,8 @@ use JsonSerializable; use MongoDB\BSON\Serializable; use MongoDB\BSON\Unserializable; +use ReturnTypeWillChange; + use function array_values; use function MongoDB\recursive_copy; @@ -29,8 +31,6 @@ * * The internal data will be filtered through array_values() during BSON * serialization to ensure that it becomes a BSON array. - * - * @api */ class BSONArray extends ArrayObject implements JsonSerializable, Serializable, Unserializable { @@ -47,9 +47,8 @@ public function __clone() /** * Factory method for var_export(). * - * @see http://php.net/oop5.magic#object.set-state - * @see http://php.net/var-export - * @param array $properties + * @see https://php.net/oop5.magic#object.set-state + * @see https://php.net/var-export * @return self */ public static function __set_state(array $properties) @@ -66,9 +65,10 @@ public static function __set_state(array $properties) * The array data will be numerically reindexed to ensure that it is stored * as a BSON array. * - * @see http://php.net/mongodb-bson-serializable.bsonserialize + * @see https://php.net/mongodb-bson-serializable.bsonserialize * @return array */ + #[ReturnTypeWillChange] public function bsonSerialize() { return array_values($this->getArrayCopy()); @@ -77,9 +77,10 @@ public function bsonSerialize() /** * Unserialize the document to BSON. * - * @see http://php.net/mongodb-bson-unserializable.bsonunserialize + * @see https://php.net/mongodb-bson-unserializable.bsonunserialize * @param array $data Array data */ + #[ReturnTypeWillChange] public function bsonUnserialize(array $data) { self::__construct($data); @@ -91,9 +92,10 @@ public function bsonUnserialize(array $data) * The array data will be numerically reindexed to ensure that it is stored * as a JSON array. * - * @see http://php.net/jsonserializable.jsonserialize + * @see https://php.net/jsonserializable.jsonserialize * @return array */ + #[ReturnTypeWillChange] public function jsonSerialize() { return array_values($this->getArrayCopy()); diff --git a/src/Model/BSONDocument.php b/src/Model/BSONDocument.php index b00ac4894..56b7787bb 100644 --- a/src/Model/BSONDocument.php +++ b/src/Model/BSONDocument.php @@ -6,7 +6,7 @@ * 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 + * https://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, @@ -21,6 +21,8 @@ use JsonSerializable; use MongoDB\BSON\Serializable; use MongoDB\BSON\Unserializable; +use ReturnTypeWillChange; + use function MongoDB\recursive_copy; /** @@ -28,8 +30,6 @@ * * The internal data will be cast to an object during BSON serialization to * ensure that it becomes a BSON document. - * - * @api */ class BSONDocument extends ArrayObject implements JsonSerializable, Serializable, Unserializable { @@ -47,22 +47,18 @@ public function __clone() * This overrides the parent constructor to allow property access of entries * by default. * - * @see http://php.net/arrayobject.construct - * @param array $input - * @param integer $flags - * @param string $iterator_class + * @see https://php.net/arrayobject.construct */ - public function __construct($input = [], $flags = ArrayObject::ARRAY_AS_PROPS, $iterator_class = 'ArrayIterator') + public function __construct(array $input = [], int $flags = ArrayObject::ARRAY_AS_PROPS, string $iteratorClass = 'ArrayIterator') { - parent::__construct($input, $flags, $iterator_class); + parent::__construct($input, $flags, $iteratorClass); } /** * Factory method for var_export(). * - * @see http://php.net/oop5.magic#object.set-state - * @see http://php.net/var-export - * @param array $properties + * @see https://php.net/oop5.magic#object.set-state + * @see https://php.net/var-export * @return self */ public static function __set_state(array $properties) @@ -76,9 +72,10 @@ public static function __set_state(array $properties) /** * Serialize the document to BSON. * - * @see http://php.net/mongodb-bson-serializable.bsonserialize + * @see https://php.net/mongodb-bson-serializable.bsonserialize * @return object */ + #[ReturnTypeWillChange] public function bsonSerialize() { return (object) $this->getArrayCopy(); @@ -87,9 +84,10 @@ public function bsonSerialize() /** * Unserialize the document to BSON. * - * @see http://php.net/mongodb-bson-unserializable.bsonunserialize + * @see https://php.net/mongodb-bson-unserializable.bsonunserialize * @param array $data Array data */ + #[ReturnTypeWillChange] public function bsonUnserialize(array $data) { parent::__construct($data, ArrayObject::ARRAY_AS_PROPS); @@ -98,9 +96,10 @@ public function bsonUnserialize(array $data) /** * Serialize the array to JSON. * - * @see http://php.net/jsonserializable.jsonserialize + * @see https://php.net/jsonserializable.jsonserialize * @return object */ + #[ReturnTypeWillChange] public function jsonSerialize() { return (object) $this->getArrayCopy(); diff --git a/src/Model/BSONIterator.php b/src/Model/BSONIterator.php index 093bfbe55..daf1d413e 100644 --- a/src/Model/BSONIterator.php +++ b/src/Model/BSONIterator.php @@ -1,12 +1,12 @@ current; } /** - * @see http://php.net/iterator.key + * @see https://php.net/iterator.key * @return mixed */ + #[ReturnTypeWillChange] public function key() { return $this->key; } /** - * @see http://php.net/iterator.next + * @see https://php.net/iterator.next * @return void */ + #[ReturnTypeWillChange] public function next() { $this->key++; @@ -111,9 +115,10 @@ public function next() } /** - * @see http://php.net/iterator.rewind + * @see https://php.net/iterator.rewind * @return void */ + #[ReturnTypeWillChange] public function rewind() { $this->key = 0; @@ -122,28 +127,26 @@ public function rewind() $this->advance(); } - /** - * @see http://php.net/iterator.valid - * @return boolean - */ - public function valid() + /** @see https://php.net/iterator.valid */ + #[ReturnTypeWillChange] + public function valid(): bool { return $this->current !== null; } - private function advance() + private function advance(): void { if ($this->position === $this->bufferLength) { return; } - if (($this->bufferLength - $this->position) < self::$bsonSize) { - throw new UnexpectedValueException(sprintf('Expected at least %d bytes; %d remaining', self::$bsonSize, $this->bufferLength - $this->position)); + if ($this->bufferLength - $this->position < self::BSON_SIZE) { + throw new UnexpectedValueException(sprintf('Expected at least %d bytes; %d remaining', self::BSON_SIZE, $this->bufferLength - $this->position)); } - list(,$documentLength) = unpack('V', substr($this->buffer, $this->position, self::$bsonSize)); + [, $documentLength] = unpack('V', substr($this->buffer, $this->position, self::BSON_SIZE)); - if (($this->bufferLength - $this->position) < $documentLength) { + if ($this->bufferLength - $this->position < $documentLength) { throw new UnexpectedValueException(sprintf('Expected %d bytes; %d remaining', $documentLength, $this->bufferLength - $this->position)); } diff --git a/src/Model/CachingIterator.php b/src/Model/CachingIterator.php index 3eb94b22d..d1ad098d4 100644 --- a/src/Model/CachingIterator.php +++ b/src/Model/CachingIterator.php @@ -1,12 +1,12 @@ iterator = new IteratorIterator($traversable); + $this->iterator = $traversable instanceof Iterator ? $traversable : new IteratorIterator($traversable); $this->iterator->rewind(); $this->storeCurrentItem(); } - /** - * @see http://php.net/countable.count - * @return integer - */ - public function count() + /** @see https://php.net/countable.count */ + public function count(): int { $this->exhaustIterator(); @@ -78,28 +79,31 @@ public function count() } /** - * @see http://php.net/iterator.current + * @see https://php.net/iterator.current * @return mixed */ + #[ReturnTypeWillChange] public function current() { - return current($this->items); + $currentItem = current($this->items); + + return $currentItem !== false ? $currentItem[self::FIELD_VALUE] : false; } /** - * @see http://php.net/iterator.key + * @see https://php.net/iterator.key * @return mixed */ + #[ReturnTypeWillChange] public function key() { - return key($this->items); + $currentItem = current($this->items); + + return $currentItem !== false ? $currentItem[self::FIELD_KEY] : null; } - /** - * @see http://php.net/iterator.next - * @return void - */ - public function next() + /** @see https://php.net/iterator.next */ + public function next(): void { if (! $this->iteratorExhausted) { $this->iteratorAdvanced = true; @@ -113,11 +117,8 @@ public function next() next($this->items); } - /** - * @see http://php.net/iterator.rewind - * @return void - */ - public function rewind() + /** @see https://php.net/iterator.rewind */ + public function rewind(): void { /* If the iterator has advanced, exhaust it now so that future iteration * can rely on the cache. @@ -129,11 +130,8 @@ public function rewind() reset($this->items); } - /** - * @see http://php.net/iterator.valid - * @return boolean - */ - public function valid() + /** @see https://php.net/iterator.valid */ + public function valid(): bool { return $this->key() !== null; } @@ -141,7 +139,7 @@ public function valid() /** * Ensures that the inner iterator is fully consumed and cached. */ - private function exhaustIterator() + private function exhaustIterator(): void { while (! $this->iteratorExhausted) { $this->next(); @@ -151,14 +149,16 @@ private function exhaustIterator() /** * Stores the current item in the cache. */ - private function storeCurrentItem() + private function storeCurrentItem(): void { - $key = $this->iterator->key(); - - if ($key === null) { + if (! $this->iterator->valid()) { return; } - $this->items[$key] = $this->iterator->current(); + // Storing a new item in the internal cache + $this->items[] = [ + self::FIELD_KEY => $this->iterator->key(), + self::FIELD_VALUE => $this->iterator->current(), + ]; } } diff --git a/src/Model/CallbackIterator.php b/src/Model/CallbackIterator.php index 23b7c1035..a900cd453 100644 --- a/src/Model/CallbackIterator.php +++ b/src/Model/CallbackIterator.php @@ -1,12 +1,12 @@ iterator = new IteratorIterator($traversable); + $this->iterator = $traversable instanceof Iterator ? $traversable : new IteratorIterator($traversable); $this->callback = $callback; } /** - * @see http://php.net/iterator.current + * @see https://php.net/iterator.current * @return mixed */ + #[ReturnTypeWillChange] public function current() { return ($this->callback)($this->iterator->current()); } /** - * @see http://php.net/iterator.key + * @see https://php.net/iterator.key * @return mixed */ + #[ReturnTypeWillChange] public function key() { return $this->iterator->key(); } - /** - * @see http://php.net/iterator.next - * @return void - */ - public function next() + /** @see https://php.net/iterator.next */ + public function next(): void { $this->iterator->next(); } - /** - * @see http://php.net/iterator.rewind - * @return void - */ - public function rewind() + /** @see https://php.net/iterator.rewind */ + public function rewind(): void { $this->iterator->rewind(); } - /** - * @see http://php.net/iterator.valid - * @return boolean - */ - public function valid() + /** @see https://php.net/iterator.valid */ + public function valid(): bool { return $this->iterator->valid(); } diff --git a/src/Model/ChangeStreamIterator.php b/src/Model/ChangeStreamIterator.php index 6f0a1a89d..23dc33aa9 100644 --- a/src/Model/ChangeStreamIterator.php +++ b/src/Model/ChangeStreamIterator.php @@ -1,12 +1,12 @@ batchSize = $firstBatchSize; @@ -98,24 +89,24 @@ public function __construct(Cursor $cursor, $firstBatchSize, $initialResumeToken } /** @internal */ - final public function commandFailed(CommandFailedEvent $event) + final public function commandFailed(CommandFailedEvent $event): void { } /** @internal */ - final public function commandStarted(CommandStartedEvent $event) + final public function commandStarted(CommandStartedEvent $event): void { if ($event->getCommandName() !== 'getMore') { return; } $this->batchPosition = 0; - $this->batchSize = null; + $this->batchSize = 0; $this->postBatchResumeToken = null; } /** @internal */ - final public function commandSucceeded(CommandSucceededEvent $event) + final public function commandSucceeded(CommandSucceededEvent $event): void { if ($event->getCommandName() !== 'getMore') { return; @@ -138,11 +129,26 @@ final public function commandSucceeded(CommandSucceededEvent $event) * @see https://php.net/iteratoriterator.current * @return mixed */ + #[ReturnTypeWillChange] public function current() { return $this->isValid ? parent::current() : null; } + /** + * Necessary to let psalm know that we're always expecting a cursor as inner + * iterator. This could be side-stepped due to the class not being final, + * but it's very much an invalid use-case. This method can be dropped in 2.0 + * once the class is final. + */ + final public function getInnerIterator(): Cursor + { + $cursor = parent::getInnerIterator(); + assert($cursor instanceof Cursor); + + return $cursor; + } + /** * Returns the resume token for the iterator's current position. * @@ -160,7 +166,7 @@ public function getResumeToken() /** * Returns the server the cursor is running on. */ - public function getServer() : Server + public function getServer(): Server { return $this->server; } @@ -169,16 +175,14 @@ public function getServer() : Server * @see https://php.net/iteratoriterator.key * @return mixed */ + #[ReturnTypeWillChange] public function key() { return $this->isValid ? parent::key() : null; } - /** - * @see https://php.net/iteratoriterator.rewind - * @return void - */ - public function next() + /** @see https://php.net/iteratoriterator.rewind */ + public function next(): void { /* Determine if advancing the iterator will execute a getMore command * (i.e. we are already positioned at the end of the current batch). If @@ -193,6 +197,7 @@ public function next() try { parent::next(); + $this->onIteration(! $getMore); } finally { if ($getMore) { @@ -201,25 +206,20 @@ public function next() } } - /** - * @see https://php.net/iteratoriterator.rewind - * @return void - */ - public function rewind() + /** @see https://php.net/iteratoriterator.rewind */ + public function rewind(): void { if ($this->isRewindNop) { return; } parent::rewind(); + $this->onIteration(false); } - /** - * @see https://php.net/iteratoriterator.valid - * @return boolean - */ - public function valid() + /** @see https://php.net/iteratoriterator.valid */ + public function valid(): bool { return $this->isValid; } @@ -248,11 +248,13 @@ private function extractResumeToken($document) if (! isset($resumeToken)) { $this->isValid = false; + throw ResumeTokenException::notFound(); } if (! is_array($resumeToken) && ! is_object($resumeToken)) { $this->isValid = false; + throw ResumeTokenException::invalidType($resumeToken); } @@ -261,10 +263,8 @@ private function extractResumeToken($document) /** * Return whether the iterator is positioned at the end of the batch. - * - * @return boolean */ - private function isAtEndOfBatch() + private function isAtEndOfBatch(): bool { return $this->batchPosition + 1 >= $this->batchSize; } @@ -273,9 +273,8 @@ private function isAtEndOfBatch() * Perform housekeeping after an iteration event. * * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#updating-the-cached-resume-token - * @param boolean $incrementBatchPosition */ - private function onIteration($incrementBatchPosition) + private function onIteration(bool $incrementBatchPosition): void { $this->isValid = parent::valid(); diff --git a/src/Model/CollectionInfo.php b/src/Model/CollectionInfo.php index f4d61df2b..4940a5e00 100644 --- a/src/Model/CollectionInfo.php +++ b/src/Model/CollectionInfo.php @@ -1,12 +1,12 @@ info = $info; @@ -48,7 +47,7 @@ public function __construct(array $info) /** * Return the collection info as an array. * - * @see http://php.net/oop5.magic#language.oop5.magic.debuginfo + * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo * @return array */ public function __debugInfo() @@ -59,6 +58,8 @@ public function __debugInfo() /** * Return the maximum number of documents to keep in the capped collection. * + * @deprecated 1.0 Deprecated in favor of using getOptions + * * @return integer|null */ public function getCappedMax() @@ -70,6 +71,8 @@ public function getCappedMax() /** * Return the maximum size (in bytes) of the capped collection. * + * @deprecated 1.0 Deprecated in favor of using getOptions + * * @return integer|null */ public function getCappedSize() @@ -78,9 +81,28 @@ public function getCappedSize() return isset($this->info['options']['size']) ? (integer) $this->info['options']['size'] : null; } + /** + * Return information about the _id index for the collection. + */ + public function getIdIndex(): array + { + return (array) ($this->info['idIndex'] ?? []); + } + + /** + * Return the "info" property of the server response. + * + * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output + */ + public function getInfo(): array + { + return (array) ($this->info['info'] ?? []); + } + /** * Return the collection name. * + * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output * @return string */ public function getName() @@ -91,16 +113,29 @@ public function getName() /** * Return the collection options. * + * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output * @return array */ public function getOptions() { - return isset($this->info['options']) ? (array) $this->info['options'] : []; + return (array) ($this->info['options'] ?? []); + } + + /** + * Return the collection type. + * + * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output + */ + public function getType(): string + { + return (string) $this->info['type']; } /** * Return whether the collection is a capped collection. * + * @deprecated 1.0 Deprecated in favor of using getOptions + * * @return boolean */ public function isCapped() @@ -111,10 +146,11 @@ public function isCapped() /** * Check whether a field exists in the collection information. * - * @see http://php.net/arrayaccess.offsetexists + * @see https://php.net/arrayaccess.offsetexists * @param mixed $key * @return boolean */ + #[ReturnTypeWillChange] public function offsetExists($key) { return array_key_exists($key, $this->info); @@ -123,10 +159,11 @@ public function offsetExists($key) /** * Return the field's value from the collection information. * - * @see http://php.net/arrayaccess.offsetget + * @see https://php.net/arrayaccess.offsetget * @param mixed $key * @return mixed */ + #[ReturnTypeWillChange] public function offsetGet($key) { return $this->info[$key]; @@ -135,11 +172,13 @@ public function offsetGet($key) /** * Not supported. * - * @see http://php.net/arrayaccess.offsetset + * @see https://php.net/arrayaccess.offsetset * @param mixed $key * @param mixed $value * @throws BadMethodCallException + * @return void */ + #[ReturnTypeWillChange] public function offsetSet($key, $value) { throw BadMethodCallException::classIsImmutable(self::class); @@ -148,10 +187,12 @@ public function offsetSet($key, $value) /** * Not supported. * - * @see http://php.net/arrayaccess.offsetunset + * @see https://php.net/arrayaccess.offsetunset * @param mixed $key * @throws BadMethodCallException + * @return void */ + #[ReturnTypeWillChange] public function offsetUnset($key) { throw BadMethodCallException::classIsImmutable(self::class); diff --git a/src/Model/CollectionInfoCommandIterator.php b/src/Model/CollectionInfoCommandIterator.php index 5cfa904b6..96c2d73a9 100644 --- a/src/Model/CollectionInfoCommandIterator.php +++ b/src/Model/CollectionInfoCommandIterator.php @@ -1,12 +1,12 @@ info = $info; @@ -47,7 +46,7 @@ public function __construct(array $info) /** * Return the database info as an array. * - * @see http://php.net/oop5.magic#language.oop5.magic.debuginfo + * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo * @return array */ public function __debugInfo() @@ -89,10 +88,11 @@ public function isEmpty() /** * Check whether a field exists in the database information. * - * @see http://php.net/arrayaccess.offsetexists + * @see https://php.net/arrayaccess.offsetexists * @param mixed $key * @return boolean */ + #[ReturnTypeWillChange] public function offsetExists($key) { return array_key_exists($key, $this->info); @@ -101,10 +101,11 @@ public function offsetExists($key) /** * Return the field's value from the database information. * - * @see http://php.net/arrayaccess.offsetget + * @see https://php.net/arrayaccess.offsetget * @param mixed $key * @return mixed */ + #[ReturnTypeWillChange] public function offsetGet($key) { return $this->info[$key]; @@ -113,11 +114,13 @@ public function offsetGet($key) /** * Not supported. * - * @see http://php.net/arrayaccess.offsetset + * @see https://php.net/arrayaccess.offsetset * @param mixed $key * @param mixed $value * @throws BadMethodCallException + * @return void */ + #[ReturnTypeWillChange] public function offsetSet($key, $value) { throw BadMethodCallException::classIsImmutable(self::class); @@ -126,10 +129,12 @@ public function offsetSet($key, $value) /** * Not supported. * - * @see http://php.net/arrayaccess.offsetunset + * @see https://php.net/arrayaccess.offsetunset * @param mixed $key * @throws BadMethodCallException + * @return void */ + #[ReturnTypeWillChange] public function offsetUnset($key) { throw BadMethodCallException::classIsImmutable(self::class); diff --git a/src/Model/DatabaseInfoIterator.php b/src/Model/DatabaseInfoIterator.php index 91576eb1b..e135de368 100644 --- a/src/Model/DatabaseInfoIterator.php +++ b/src/Model/DatabaseInfoIterator.php @@ -1,12 +1,12 @@ databases = $databases; @@ -49,10 +46,9 @@ public function __construct(array $databases) * Return the current element as a DatabaseInfo instance. * * @see DatabaseInfoIterator::current() - * @see http://php.net/iterator.current - * @return DatabaseInfo + * @see https://php.net/iterator.current */ - public function current() + public function current(): DatabaseInfo { return new DatabaseInfo(current($this->databases)); } @@ -60,10 +56,9 @@ public function current() /** * Return the key of the current element. * - * @see http://php.net/iterator.key - * @return integer + * @see https://php.net/iterator.key */ - public function key() + public function key(): int { return key($this->databases); } @@ -71,9 +66,9 @@ public function key() /** * Move forward to next element. * - * @see http://php.net/iterator.next + * @see https://php.net/iterator.next */ - public function next() + public function next(): void { next($this->databases); } @@ -81,9 +76,9 @@ public function next() /** * Rewind the Iterator to the first element. * - * @see http://php.net/iterator.rewind + * @see https://php.net/iterator.rewind */ - public function rewind() + public function rewind(): void { reset($this->databases); } @@ -91,10 +86,9 @@ public function rewind() /** * Checks if current position is valid. * - * @see http://php.net/iterator.valid - * @return boolean + * @see https://php.net/iterator.valid */ - public function valid() + public function valid(): bool { return key($this->databases) !== null; } diff --git a/src/Model/IndexInfo.php b/src/Model/IndexInfo.php index ee8a29357..4d86bab16 100644 --- a/src/Model/IndexInfo.php +++ b/src/Model/IndexInfo.php @@ -1,12 +1,12 @@ info = $info; @@ -53,7 +55,7 @@ public function __construct(array $info) /** * Return the collection info as an array. * - * @see http://php.net/oop5.magic#language.oop5.magic.debuginfo + * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo * @return array */ public function __debugInfo() @@ -125,16 +127,19 @@ public function is2dSphere() * Return whether or not this index is of type geoHaystack. * * @return boolean + * @deprecated Since 1.16: MongoDB 5.0 removes support for geoHaystack indexes. */ public function isGeoHaystack() { + trigger_error('MongoDB 5.0 removes support for "geoHaystack" indexes, the method "IndexInfo::isGeoHaystack()" will be removed in a future release', E_USER_DEPRECATED); + return array_search('geoHaystack', $this->getKey(), true) !== false; } /** * Return whether this is a sparse index. * - * @see http://docs.mongodb.org/manual/core/index-sparse/ + * @see https://mongodb.com/docs/manual/core/index-sparse/ * @return boolean */ public function isSparse() @@ -155,7 +160,7 @@ public function isText() /** * Return whether this is a TTL index. * - * @see http://docs.mongodb.org/manual/core/index-ttl/ + * @see https://mongodb.com/docs/manual/core/index-ttl/ * @return boolean */ public function isTtl() @@ -166,7 +171,7 @@ public function isTtl() /** * Return whether this is a unique index. * - * @see http://docs.mongodb.org/manual/core/index-unique/ + * @see https://mongodb.com/docs/manual/core/index-unique/ * @return boolean */ public function isUnique() @@ -177,10 +182,11 @@ public function isUnique() /** * Check whether a field exists in the index information. * - * @see http://php.net/arrayaccess.offsetexists + * @see https://php.net/arrayaccess.offsetexists * @param mixed $key * @return boolean */ + #[ReturnTypeWillChange] public function offsetExists($key) { return array_key_exists($key, $this->info); @@ -193,11 +199,12 @@ public function offsetExists($key) * that index fields be made accessible under their original names. It may * also be used to access fields that do not have a helper method. * - * @see http://php.net/arrayaccess.offsetget + * @see https://php.net/arrayaccess.offsetget * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst#getting-full-index-information * @param mixed $key * @return mixed */ + #[ReturnTypeWillChange] public function offsetGet($key) { return $this->info[$key]; @@ -206,11 +213,13 @@ public function offsetGet($key) /** * Not supported. * - * @see http://php.net/arrayaccess.offsetset + * @see https://php.net/arrayaccess.offsetset * @param mixed $key * @param mixed $value * @throws BadMethodCallException + * @return void */ + #[ReturnTypeWillChange] public function offsetSet($key, $value) { throw BadMethodCallException::classIsImmutable(self::class); @@ -219,10 +228,12 @@ public function offsetSet($key, $value) /** * Not supported. * - * @see http://php.net/arrayaccess.offsetunset + * @see https://php.net/arrayaccess.offsetunset * @param mixed $key * @throws BadMethodCallException + * @return void */ + #[ReturnTypeWillChange] public function offsetUnset($key) { throw BadMethodCallException::classIsImmutable(self::class); diff --git a/src/Model/IndexInfoIterator.php b/src/Model/IndexInfoIterator.php index 5195172c4..26e2d3d30 100644 --- a/src/Model/IndexInfoIterator.php +++ b/src/Model/IndexInfoIterator.php @@ -1,12 +1,12 @@ generateIndexName($index['key']); } if (! is_string($index['name'])) { @@ -75,10 +76,8 @@ public function __construct(array $index) /** * Return the index name. - * - * @return string */ - public function __toString() + public function __toString(): string { return $this->index['name']; } @@ -87,11 +86,30 @@ public function __toString() * Serialize the index information to BSON for index creation. * * @see \MongoDB\Collection::createIndexes() - * @see http://php.net/mongodb-bson-serializable.bsonserialize - * @return array + * @see https://php.net/mongodb-bson-serializable.bsonserialize */ - public function bsonSerialize() + public function bsonSerialize(): array { return $this->index; } + + /** + * Generate an index name from a key specification. + * + * @param array|object $document Document containing fields mapped to values, + * which denote order or an index type + * @throws InvalidArgumentException if $document is not an array or object + */ + private function generateIndexName($document): string + { + $document = document_to_array($document); + + $name = ''; + + foreach ($document as $field => $type) { + $name .= ($name !== '' ? '_' : '') . $field . '_' . $type; + } + + return $name; + } } diff --git a/src/Operation/Aggregate.php b/src/Operation/Aggregate.php index 85df84b44..4d6c5867c 100644 --- a/src/Operation/Aggregate.php +++ b/src/Operation/Aggregate.php @@ -1,12 +1,12 @@ $operation) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$pipeline is not a list (unexpected index: "%s")', $i)); - } - - if (! is_array($operation) && ! is_object($operation)) { - throw InvalidArgumentException::invalidType(sprintf('$pipeline[%d]', $i), $operation, 'array or object'); - } - - $expectedIndex += 1; + if (! is_pipeline($pipeline, true /* allowEmpty */)) { + throw new InvalidArgumentException('$pipeline is not a valid aggregation pipeline'); } - $options += [ - 'allowDiskUse' => false, - 'useCursor' => true, - ]; + $options += ['useCursor' => true]; - if (! is_bool($options['allowDiskUse'])) { + if (isset($options['allowDiskUse']) && ! is_bool($options['allowDiskUse'])) { throw InvalidArgumentException::invalidType('"allowDiskUse" option', $options['allowDiskUse'], 'boolean'); } @@ -184,10 +159,6 @@ public function __construct($databaseName, $collectionName, array $pipeline, arr throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); } - if (isset($options['comment']) && ! is_string($options['comment'])) { - throw InvalidArgumentException::invalidType('"comment" option', $options['comment'], 'string'); - } - if (isset($options['explain']) && ! is_bool($options['explain'])) { throw InvalidArgumentException::invalidType('"explain" option', $options['explain'], 'boolean'); } @@ -196,6 +167,10 @@ public function __construct($databaseName, $collectionName, array $pipeline, arr throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object'); } + if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { + throw InvalidArgumentException::invalidType('"let" option', $options['let'], ['array', 'object']); + } + if (isset($options['maxAwaitTimeMS']) && ! is_integer($options['maxAwaitTimeMS'])) { throw InvalidArgumentException::invalidType('"maxAwaitTimeMS" option', $options['maxAwaitTimeMS'], 'integer'); } @@ -232,6 +207,10 @@ public function __construct($databaseName, $collectionName, array $pipeline, arr throw new InvalidArgumentException('"batchSize" option should not be used if "useCursor" is false'); } + if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { + unset($options['bypassDocumentValidation']); + } + if (isset($options['readConcern']) && $options['readConcern']->isDefault()) { unset($options['readConcern']); } @@ -240,12 +219,23 @@ public function __construct($databaseName, $collectionName, array $pipeline, arr unset($options['writeConcern']); } - if (! empty($options['explain'])) { + $this->isExplain = ! empty($options['explain']); + $this->isWrite = is_last_pipeline_operator_write($pipeline) && ! $this->isExplain; + + // Explain does not use a cursor + if ($this->isExplain) { $options['useCursor'] = false; + unset($options['batchSize']); + } + + /* Ignore batchSize for writes, since no documents are returned and a + * batchSize of zero could prevent the pipeline from executing. */ + if ($this->isWrite) { + unset($options['batchSize']); } - $this->databaseName = (string) $databaseName; - $this->collectionName = isset($collectionName) ? (string) $collectionName : null; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->pipeline = $pipeline; $this->options = $options; } @@ -254,50 +244,32 @@ public function __construct($databaseName, $collectionName, array $pipeline, arr * Execute the operation. * * @see Executable::execute() - * @param Server $server - * @return Traversable + * @return ArrayIterator|Cursor * @throws UnexpectedValueException if the command response was malformed - * @throws UnsupportedException if collation, read concern, or write concern is used and unsupported + * @throws UnsupportedException if read concern or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { - throw UnsupportedException::collationNotSupported(); - } - - if (isset($this->options['readConcern']) && ! server_supports_feature($server, self::$wireVersionForReadConcern)) { - throw UnsupportedException::readConcernNotSupported(); - } - - if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) { - throw UnsupportedException::writeConcernNotSupported(); - } - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction) { if (isset($this->options['readConcern'])) { throw UnsupportedException::readConcernNotSupportedInTransaction(); } + if (isset($this->options['writeConcern'])) { throw UnsupportedException::writeConcernNotSupportedInTransaction(); } } - $hasExplain = ! empty($this->options['explain']); - $hasWriteStage = $this->hasWriteStage(); - $command = new Command( - $this->createCommandDocument($server, $hasWriteStage), + $this->createCommandDocument(), $this->createCommandOptions() ); - $options = $this->createOptions($hasWriteStage, $hasExplain); - $cursor = $hasWriteStage && ! $hasExplain - ? $server->executeReadWriteCommand($this->databaseName, $command, $options) - : $server->executeReadCommand($this->databaseName, $command, $options); + $cursor = $this->executeCommand($server, $command); - if ($this->options['useCursor'] || $hasExplain) { + if ($this->options['useCursor'] || $this->isExplain) { if (isset($this->options['typeMap'])) { $cursor->setTypeMap($this->options['typeMap']); } @@ -311,41 +283,51 @@ public function execute(Server $server) $result = current($cursor->toArray()); - if (! isset($result->result) || ! is_array($result->result)) { + if (! is_object($result) || ! isset($result->result) || ! is_array($result->result)) { throw new UnexpectedValueException('aggregate command did not return a "result" array'); } return new ArrayIterator($result->result); } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->createCommandDocument($server, $this->hasWriteStage()); + $cmd = $this->createCommandDocument(); + + // Read concern can change the query plan + if (isset($this->options['readConcern'])) { + $cmd['readConcern'] = $this->options['readConcern']; + } + + return $cmd; } - private function createCommandDocument(Server $server, bool $hasWriteStage) : array + /** + * Create the aggregate command document. + */ + private function createCommandDocument(): array { $cmd = [ 'aggregate' => $this->collectionName ?? 1, 'pipeline' => $this->pipeline, ]; - $cmd['allowDiskUse'] = $this->options['allowDiskUse']; - - if (! empty($this->options['bypassDocumentValidation']) && - server_supports_feature($server, self::$wireVersionForDocumentLevelValidation) - ) { - $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; - } - - foreach (['comment', 'explain', 'maxTimeMS'] as $option) { + foreach (['allowDiskUse', 'bypassDocumentValidation', 'comment', 'explain', 'maxTimeMS'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = $this->options[$option]; } } - if (isset($this->options['collation'])) { - $cmd['collation'] = (object) $this->options['collation']; + foreach (['collation', 'let'] as $option) { + if (isset($this->options[$option])) { + $cmd[$option] = (object) $this->options[$option]; + } } if (isset($this->options['hint'])) { @@ -353,10 +335,7 @@ private function createCommandDocument(Server $server, bool $hasWriteStage) : ar } if ($this->options['useCursor']) { - /* Ignore batchSize if pipeline includes an $out or $merge stage, as - * no documents will be returned and sending a batchSize of zero - * could prevent the pipeline from executing at all. */ - $cmd['cursor'] = isset($this->options["batchSize"]) && ! $hasWriteStage + $cmd['cursor'] = isset($this->options["batchSize"]) ? ['batchSize' => $this->options["batchSize"]] : new stdClass(); } @@ -364,7 +343,7 @@ private function createCommandDocument(Server $server, bool $hasWriteStage) : ar return $cmd; } - private function createCommandOptions() : array + private function createCommandOptions(): array { $cmdOptions = []; @@ -376,39 +355,38 @@ private function createCommandOptions() : array } /** - * Create options for executing the command. + * Execute the aggregate command using the appropriate Server method. * - * @see http://php.net/manual/en/mongodb-driver-server.executereadcommand.php - * @see http://php.net/manual/en/mongodb-driver-server.executereadwritecommand.php - * @param boolean $hasWriteStage - * @param boolean $hasExplain - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php + * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php + * @see https://php.net/manual/en/mongodb-driver-server.executereadwritecommand.php */ - private function createOptions($hasWriteStage, $hasExplain) + private function executeCommand(Server $server, Command $command): Cursor { $options = []; - if (isset($this->options['readConcern'])) { - $options['readConcern'] = $this->options['readConcern']; + foreach (['readConcern', 'readPreference', 'session'] as $option) { + if (isset($this->options[$option])) { + $options[$option] = $this->options[$option]; + } } - if (! $hasWriteStage && isset($this->options['readPreference'])) { - $options['readPreference'] = $this->options['readPreference']; + if ($this->isWrite && isset($this->options['writeConcern'])) { + $options['writeConcern'] = $this->options['writeConcern']; } - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; + if (! $this->isWrite) { + return $server->executeReadCommand($this->databaseName, $command, $options); } - if ($hasWriteStage && ! $hasExplain && isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; + /* Server::executeReadWriteCommand() does not support a "readPreference" + * option, so fall back to executeCommand(). This means that libmongoc + * will not apply any client-level options (e.g. writeConcern), but that + * should not be an issue as PHPLIB handles inheritance on its own. */ + if (isset($options['readPreference'])) { + return $server->executeCommand($this->databaseName, $command, $options); } - return $options; - } - - private function hasWriteStage() : bool - { - return is_last_pipeline_operator_write($this->pipeline); + return $server->executeReadWriteCommand($this->databaseName, $command, $options); } } diff --git a/src/Operation/BulkWrite.php b/src/Operation/BulkWrite.php index 95bd4bb1c..e104d5d73 100644 --- a/src/Operation/BulkWrite.php +++ b/src/Operation/BulkWrite.php @@ -1,12 +1,12 @@ $operation) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$operations is not a list (unexpected index: "%s")', $i)); - } - if (! is_array($operation)) { throw InvalidArgumentException::invalidType(sprintf('$operations[%d]', $i), $operation, 'array'); } @@ -194,12 +173,8 @@ public function __construct($databaseName, $collectionName, array $operations, a $args[1]['limit'] = ($type === self::DELETE_ONE ? 1 : 0); - if (isset($args[1]['collation'])) { - $this->isCollationUsed = true; - - if (! is_array($args[1]['collation']) && ! is_object($args[1]['collation'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]["collation"]', $i, $type), $args[1]['collation'], 'array or object'); - } + if (isset($args[1]['collation']) && ! is_array($args[1]['collation']) && ! is_object($args[1]['collation'])) { + throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]["collation"]', $i, $type), $args[1]['collation'], 'array or object'); } $operations[$i][$type][1] = $args[1]; @@ -215,10 +190,19 @@ public function __construct($databaseName, $collectionName, array $operations, a throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array or object'); } + // Treat empty arrays as replacement documents for BC + if ($args[1] === []) { + $args[1] = (object) $args[1]; + } + if (is_first_key_operator($args[1])) { throw new InvalidArgumentException(sprintf('First key in $operations[%d]["%s"][1] is an update operator', $i, $type)); } + if (is_pipeline($args[1], true /* allowEmpty */)) { + throw new InvalidArgumentException(sprintf('$operations[%d]["%s"][1] is an update pipeline', $i, $type)); + } + if (! isset($args[2])) { $args[2] = []; } @@ -230,12 +214,8 @@ public function __construct($databaseName, $collectionName, array $operations, a $args[2]['multi'] = false; $args[2] += ['upsert' => false]; - if (isset($args[2]['collation'])) { - $this->isCollationUsed = true; - - if (! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object'); - } + if (isset($args[2]['collation']) && ! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) { + throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object'); } if (! is_bool($args[2]['upsert'])) { @@ -257,7 +237,7 @@ public function __construct($databaseName, $collectionName, array $operations, a } if (! is_first_key_operator($args[1]) && ! is_pipeline($args[1])) { - throw new InvalidArgumentException(sprintf('First key in $operations[%d]["%s"][1] is neither an update operator nor a pipeline', $i, $type)); + throw new InvalidArgumentException(sprintf('Expected update operator(s) or non-empty pipeline for $operations[%d]["%s"][1]', $i, $type)); } if (! isset($args[2])) { @@ -271,20 +251,12 @@ public function __construct($databaseName, $collectionName, array $operations, a $args[2]['multi'] = ($type === self::UPDATE_MANY); $args[2] += ['upsert' => false]; - if (isset($args[2]['arrayFilters'])) { - $this->isArrayFiltersUsed = true; - - if (! is_array($args[2]['arrayFilters'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["arrayFilters"]', $i, $type), $args[2]['arrayFilters'], 'array'); - } + if (isset($args[2]['arrayFilters']) && ! is_array($args[2]['arrayFilters'])) { + throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["arrayFilters"]', $i, $type), $args[2]['arrayFilters'], 'array'); } - if (isset($args[2]['collation'])) { - $this->isCollationUsed = true; - - if (! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object'); - } + if (isset($args[2]['collation']) && ! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) { + throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object'); } if (! is_bool($args[2]['upsert'])) { @@ -298,8 +270,6 @@ public function __construct($databaseName, $collectionName, array $operations, a default: throw new InvalidArgumentException(sprintf('Unknown operation type "%s" in $operations[%d]', $type, $i)); } - - $expectedIndex += 1; } $options += ['ordered' => true]; @@ -320,12 +290,20 @@ public function __construct($databaseName, $collectionName, array $operations, a throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); } + if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { + throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object'); + } + + if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { + unset($options['bypassDocumentValidation']); + } + if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { unset($options['writeConcern']); } - $this->databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->operations = $operations; $this->options = $options; } @@ -334,35 +312,18 @@ public function __construct($databaseName, $collectionName, array $operations, a * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return BulkWriteResult - * @throws UnsupportedException if array filters or collation is used and unsupported + * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if ($this->isArrayFiltersUsed && ! server_supports_feature($server, self::$wireVersionForArrayFilters)) { - throw UnsupportedException::arrayFiltersNotSupported(); - } - - if ($this->isCollationUsed && ! server_supports_feature($server, self::$wireVersionForCollation)) { - throw UnsupportedException::collationNotSupported(); - } - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { throw UnsupportedException::writeConcernNotSupportedInTransaction(); } - $options = ['ordered' => $this->options['ordered']]; - - if (! empty($this->options['bypassDocumentValidation']) && - server_supports_feature($server, self::$wireVersionForDocumentLevelValidation) - ) { - $options['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; - } - - $bulk = new Bulk($options); + $bulk = new Bulk($this->createBulkWriteOptions()); $insertedIds = []; foreach ($this->operations as $i => $operation) { @@ -386,18 +347,39 @@ public function execute(Server $server) } } - $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createOptions()); + $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); return new BulkWriteResult($writeResult, $insertedIds); } + /** + * Create options for constructing the bulk write. + * + * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php + */ + private function createBulkWriteOptions(): array + { + $options = ['ordered' => $this->options['ordered']]; + + foreach (['bypassDocumentValidation', 'comment'] as $option) { + if (isset($this->options[$option])) { + $options[$option] = $this->options[$option]; + } + } + + if (isset($this->options['let'])) { + $options['let'] = (object) $this->options['let']; + } + + return $options; + } + /** * Create options for executing the bulk write. * - * @see http://php.net/manual/en/mongodb-driver-server.executebulkwrite.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php */ - private function createOptions() + private function createExecuteOptions(): array { $options = []; diff --git a/src/Operation/Count.php b/src/Operation/Count.php index 2e00ae555..175ab87a9 100644 --- a/src/Operation/Count.php +++ b/src/Operation/Count.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->filter = $filter; $this->options = $options; } @@ -152,22 +141,13 @@ public function __construct($databaseName, $collectionName, $filter = [], array * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return integer * @throws UnexpectedValueException if the command response was malformed - * @throws UnsupportedException if collation or read concern is used and unsupported + * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { - throw UnsupportedException::collationNotSupported(); - } - - if (isset($this->options['readConcern']) && ! server_supports_feature($server, self::$wireVersionForReadConcern)) { - throw UnsupportedException::readConcernNotSupported(); - } - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['readConcern'])) { throw UnsupportedException::readConcernNotSupportedInTransaction(); @@ -177,24 +157,35 @@ public function execute(Server $server) $result = current($cursor->toArray()); // Older server versions may return a float - if (! isset($result->n) || ! (is_integer($result->n) || is_float($result->n))) { + if (! is_object($result) || ! isset($result->n) || ! (is_integer($result->n) || is_float($result->n))) { throw new UnexpectedValueException('count command did not return a numeric "n" value'); } return (integer) $result->n; } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->createCommandDocument(); + $cmd = $this->createCommandDocument(); + + // Read concern can change the query plan + if (isset($this->options['readConcern'])) { + $cmd['readConcern'] = $this->options['readConcern']; + } + + return $cmd; } /** * Create the count command document. - * - * @return array */ - private function createCommandDocument() + private function createCommandDocument(): array { $cmd = ['count' => $this->collectionName]; @@ -210,7 +201,7 @@ private function createCommandDocument() $cmd['hint'] = is_array($this->options['hint']) ? (object) $this->options['hint'] : $this->options['hint']; } - foreach (['limit', 'maxTimeMS', 'skip'] as $option) { + foreach (['comment', 'limit', 'maxTimeMS', 'skip'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = $this->options[$option]; } @@ -222,10 +213,9 @@ private function createCommandDocument() /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executereadcommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; diff --git a/src/Operation/CountDocuments.php b/src/Operation/CountDocuments.php index 77499e5b7..d83a6bd6e 100644 --- a/src/Operation/CountDocuments.php +++ b/src/Operation/CountDocuments.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->filter = $filter; - $this->aggregateOptions = array_intersect_key($options, ['collation' => 1, 'hint' => 1, 'maxTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1]); + $this->aggregateOptions = array_intersect_key($options, ['collation' => 1, 'comment' => 1, 'hint' => 1, 'maxTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1]); $this->countOptions = array_intersect_key($options, ['limit' => 1, 'skip' => 1]); $this->aggregate = $this->createAggregate(); @@ -124,7 +122,6 @@ public function __construct($databaseName, $collectionName, $filter, array $opti * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if collation or read concern is used and unsupported @@ -133,6 +130,8 @@ public function __construct($databaseName, $collectionName, $filter, array $opti public function execute(Server $server) { $cursor = $this->aggregate->execute($server); + assert($cursor instanceof Cursor); + $allResults = $cursor->toArray(); /* If there are no documents to count, the aggregation pipeline has no items to group, and @@ -142,17 +141,14 @@ public function execute(Server $server) } $result = current($allResults); - if (! isset($result->n) || ! (is_integer($result->n) || is_float($result->n))) { + if (! is_object($result) || ! isset($result->n) || ! (is_integer($result->n) || is_float($result->n))) { throw new UnexpectedValueException('count command did not return a numeric "n" value'); } return (integer) $result->n; } - /** - * @return Aggregate - */ - private function createAggregate() + private function createAggregate(): Aggregate { $pipeline = [ ['$match' => (object) $this->filter], diff --git a/src/Operation/CreateCollection.php b/src/Operation/CreateCollection.php index 98fac51d6..eff5ab955 100644 --- a/src/Operation/CreateCollection.php +++ b/src/Operation/CreateCollection.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + if (isset($options['pipeline']) && ! is_pipeline($options['pipeline'], true /* allowEmpty */)) { + throw new InvalidArgumentException('"pipeline" option is not a valid aggregation pipeline'); + } + + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->options = $options; } @@ -204,21 +253,11 @@ public function __construct($databaseName, $collectionName, array $options = []) * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return array|object Command result document - * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { - throw UnsupportedException::collationNotSupported(); - } - - if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) { - throw UnsupportedException::writeConcernNotSupported(); - } - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); if (isset($this->options['typeMap'])) { @@ -230,20 +269,18 @@ public function execute(Server $server) /** * Create the create command. - * - * @return Command */ - private function createCommand() + private function createCommand(): Command { $cmd = ['create' => $this->collectionName]; - foreach (['autoIndexId', 'capped', 'flags', 'max', 'maxTimeMS', 'size', 'validationAction', 'validationLevel'] as $option) { + foreach (['autoIndexId', 'capped', 'comment', 'expireAfterSeconds', 'flags', 'max', 'maxTimeMS', 'pipeline', 'size', 'validationAction', 'validationLevel', 'viewOn'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = $this->options[$option]; } } - foreach (['collation', 'indexOptionDefaults', 'storageEngine', 'validator'] as $option) { + foreach (['changeStreamPreAndPostImages', 'clusteredIndex', 'collation', 'encryptedFields', 'indexOptionDefaults', 'storageEngine', 'timeseries', 'validator'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = (object) $this->options[$option]; } @@ -255,10 +292,9 @@ private function createCommand() /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executewritecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; diff --git a/src/Operation/CreateEncryptedCollection.php b/src/Operation/CreateEncryptedCollection.php new file mode 100644 index 000000000..bc08b321b --- /dev/null +++ b/src/Operation/CreateEncryptedCollection.php @@ -0,0 +1,193 @@ +createCollection = new CreateCollection($databaseName, $collectionName, $options); + + /** @psalm-var array{ecocCollection?: ?string, escCollection?: ?string} */ + $encryptedFields = document_to_array($options['encryptedFields']); + $enxcolOptions = ['clusteredIndex' => ['key' => ['_id' => 1], 'unique' => true]]; + + $this->createMetadataCollections = [ + new CreateCollection($databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc', $enxcolOptions), + new CreateCollection($databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc', $enxcolOptions), + ]; + + $this->createSafeContentIndex = new CreateIndexes($databaseName, $collectionName, [['key' => ['__safeContent__' => 1]]]); + + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; + $this->options = $options; + } + + /** + * Create data keys for any encrypted fields where "keyId" is null. + * + * This method should be called before execute(), as it may modify the + * "encryptedFields" option and reconstruct the internal CreateCollection + * operation used for creating the encrypted collection. + * + * The $encryptedFields reference parameter may be used to determine which + * data keys have been created. + * + * @see \MongoDB\Database::createEncryptedCollection() + * @see https://www.php.net/manual/en/mongodb-driver-clientencryption.createdatakey.php + * @throws DriverRuntimeException for errors creating a data key + */ + public function createDataKeys(ClientEncryption $clientEncryption, string $kmsProvider, ?array $masterKey, ?array &$encryptedFields = null): void + { + /** @psalm-var array{fields: list|Serializable|PackedArray} */ + $encryptedFields = document_to_array($this->options['encryptedFields']); + + // NOP if there are no fields to examine + if (! isset($encryptedFields['fields'])) { + return; + } + + // Allow PackedArray or Serializable object for the fields array + if ($encryptedFields['fields'] instanceof PackedArray) { + /** @psalm-var array */ + $encryptedFields['fields'] = $encryptedFields['fields']->toPHP([ + 'array' => 'array', + 'document' => 'object', + 'root' => 'array', + ]); + } elseif ($encryptedFields['fields'] instanceof Serializable) { + $encryptedFields['fields'] = $encryptedFields['fields']->bsonSerialize(); + } + + // Skip invalid types and defer to the server to raise an error + if (! is_array($encryptedFields['fields'])) { + return; + } + + $createDataKeyArgs = [ + $kmsProvider, + $masterKey !== null ? ['masterKey' => $masterKey] : [], + ]; + + foreach ($encryptedFields['fields'] as $i => $field) { + // Skip invalid types and defer to the server to raise an error + if (! is_array($field) && ! is_object($field)) { + continue; + } + + $field = document_to_array($field); + + if (array_key_exists('keyId', $field) && $field['keyId'] === null) { + $field['keyId'] = $clientEncryption->createDataKey(...$createDataKeyArgs); + $encryptedFields['fields'][$i] = $field; + } + } + + $this->options['encryptedFields'] = $encryptedFields; + $this->createCollection = new CreateCollection($this->databaseName, $this->collectionName, $this->options); + } + + /** + * @see Executable::execute() + * @return array|object Command result document from creating the encrypted collection + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + * @throws UnsupportedException if the server does not support Queryable Encryption + */ + public function execute(Server $server) + { + if (! server_supports_feature($server, self::WIRE_VERSION_FOR_QUERYABLE_ENCRYPTION_V2)) { + throw new UnsupportedException('Driver support of Queryable Encryption is incompatible with server. Upgrade server to use Queryable Encryption.'); + } + + foreach ($this->createMetadataCollections as $createMetadataCollection) { + $createMetadataCollection->execute($server); + } + + $result = $this->createCollection->execute($server); + + $this->createSafeContentIndex->execute($server); + + return $result; + } +} diff --git a/src/Operation/CreateIndexes.php b/src/Operation/CreateIndexes.php index ba53d95b7..1b8414fd8 100644 --- a/src/Operation/CreateIndexes.php +++ b/src/Operation/CreateIndexes.php @@ -1,12 +1,12 @@ $index) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$indexes is not a list (unexpected index: "%s")', $i)); - } - if (! is_array($index)) { throw InvalidArgumentException::invalidType(sprintf('$index[%d]', $i), $index, 'array'); } - if (isset($index['collation'])) { - $this->isCollationUsed = true; - } - $this->indexes[] = new IndexInput($index); - - $expectedIndex += 1; } if (isset($options['commitQuorum']) && ! is_string($options['commitQuorum']) && ! is_integer($options['commitQuorum'])) { @@ -139,8 +121,8 @@ public function __construct($databaseName, $collectionName, array $indexes, arra unset($options['writeConcern']); } - $this->databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->options = $options; } @@ -148,21 +130,12 @@ public function __construct($databaseName, $collectionName, array $indexes, arra * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return string[] The names of the created indexes - * @throws UnsupportedException if collation or write concern is used and unsupported + * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if ($this->isCollationUsed && ! server_supports_feature($server, self::$wireVersionForCollation)) { - throw UnsupportedException::collationNotSupported(); - } - - if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) { - throw UnsupportedException::writeConcernNotSupported(); - } - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { throw UnsupportedException::writeConcernNotSupportedInTransaction(); @@ -178,10 +151,9 @@ public function execute(Server $server) /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executewritecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; @@ -200,10 +172,9 @@ private function createOptions() * Create one or more indexes for the collection using the createIndexes * command. * - * @param Server $server * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - private function executeCommand(Server $server) + private function executeCommand(Server $server): void { $cmd = [ 'createIndexes' => $this->collectionName, @@ -213,15 +184,17 @@ private function executeCommand(Server $server) if (isset($this->options['commitQuorum'])) { /* Drivers MUST manually raise an error if this option is specified * when creating an index on a pre 4.4 server. */ - if (! server_supports_feature($server, self::$wireVersionForCommitQuorum)) { + if (! server_supports_feature($server, self::WIRE_VERSION_FOR_COMMIT_QUORUM)) { throw UnsupportedException::commitQuorumNotSupported(); } $cmd['commitQuorum'] = $this->options['commitQuorum']; } - if (isset($this->options['maxTimeMS'])) { - $cmd['maxTimeMS'] = $this->options['maxTimeMS']; + foreach (['comment', 'maxTimeMS'] as $option) { + if (isset($this->options[$option])) { + $cmd[$option] = $this->options[$option]; + } } $server->executeWriteCommand($this->databaseName, new Command($cmd), $this->createOptions()); diff --git a/src/Operation/DatabaseCommand.php b/src/Operation/DatabaseCommand.php index af0f76248..7c989ae07 100644 --- a/src/Operation/DatabaseCommand.php +++ b/src/Operation/DatabaseCommand.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; + $this->databaseName = $databaseName; $this->command = $command instanceof Command ? $command : new Command($command); $this->options = $options; } @@ -93,7 +91,6 @@ public function __construct($databaseName, $command, array $options = []) * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return Cursor */ public function execute(Server $server) @@ -110,10 +107,9 @@ public function execute(Server $server) /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; diff --git a/src/Operation/Delete.php b/src/Operation/Delete.php index 65cc6f6e9..5f3318012 100644 --- a/src/Operation/Delete.php +++ b/src/Operation/Delete.php @@ -1,12 +1,12 @@ isDefault()) { unset($options['writeConcern']); } - $this->databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->filter = $filter; $this->limit = $limit; $this->options = $options; @@ -135,20 +141,18 @@ public function __construct($databaseName, $collectionName, $filter, $limit, arr * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return DeleteResult + * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { - throw UnsupportedException::collationNotSupported(); - } - - /* Server versions >= 3.4.0 raise errors for unknown update - * options. For previous versions, the CRUD spec requires a client-side - * error. */ - if (isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHintServerSideError)) { + /* CRUD spec requires a client-side error when using "hint" with an + * unacknowledged write concern on an unsupported server. */ + if ( + isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) && + isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_HINT) + ) { throw UnsupportedException::hintNotSupported(); } @@ -157,7 +161,7 @@ public function execute(Server $server) throw UnsupportedException::writeConcernNotSupportedInTransaction(); } - $bulk = new Bulk(); + $bulk = new Bulk($this->createBulkWriteOptions()); $bulk->delete($this->filter, $this->createDeleteOptions()); $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); @@ -165,26 +169,54 @@ public function execute(Server $server) return new DeleteResult($writeResult); } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { $cmd = ['delete' => $this->collectionName, 'deletes' => [['q' => $this->filter] + $this->createDeleteOptions()]]; - if (isset($this->options['writeConcern'])) { - $cmd['writeConcern'] = $this->options['writeConcern']; + if (isset($this->options['comment'])) { + $cmd['comment'] = $this->options['comment']; + } + + if (isset($this->options['let'])) { + $cmd['let'] = (object) $this->options['let']; } return $cmd; } + /** + * Create options for constructing the bulk write. + * + * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php + */ + private function createBulkWriteOptions(): array + { + $options = []; + + if (isset($this->options['comment'])) { + $options['comment'] = $this->options['comment']; + } + + if (isset($this->options['let'])) { + $options['let'] = (object) $this->options['let']; + } + + return $options; + } + /** * Create options for the delete command. * * Note that these options are different from the bulk write options, which * are created in createExecuteOptions(). - * - * @return array */ - private function createDeleteOptions() + private function createDeleteOptions(): array { $deleteOptions = ['limit' => $this->limit]; @@ -202,10 +234,9 @@ private function createDeleteOptions() /** * Create options for executing the bulk write. * - * @see http://php.net/manual/en/mongodb-driver-server.executebulkwrite.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php */ - private function createExecuteOptions() + private function createExecuteOptions(): array { $options = []; diff --git a/src/Operation/DeleteMany.php b/src/Operation/DeleteMany.php index ffda516ca..087a60b6d 100644 --- a/src/Operation/DeleteMany.php +++ b/src/Operation/DeleteMany.php @@ -1,12 +1,12 @@ delete = new Delete($databaseName, $collectionName, $filter, 0, $options); } @@ -73,7 +76,6 @@ public function __construct($databaseName, $collectionName, $filter, array $opti * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return DeleteResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -83,8 +85,14 @@ public function execute(Server $server) return $this->delete->execute($server); } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->delete->getCommandDocument($server); + return $this->delete->getCommandDocument(); } } diff --git a/src/Operation/DeleteOne.php b/src/Operation/DeleteOne.php index 742a58f73..6c3133f34 100644 --- a/src/Operation/DeleteOne.php +++ b/src/Operation/DeleteOne.php @@ -1,12 +1,12 @@ delete = new Delete($databaseName, $collectionName, $filter, 1, $options); } @@ -73,7 +76,6 @@ public function __construct($databaseName, $collectionName, $filter, array $opti * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return DeleteResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -83,8 +85,14 @@ public function execute(Server $server) return $this->delete->execute($server); } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->delete->getCommandDocument($server); + return $this->delete->getCommandDocument(); } } diff --git a/src/Operation/Distinct.php b/src/Operation/Distinct.php index f8e169ca7..87fd7c597 100644 --- a/src/Operation/Distinct.php +++ b/src/Operation/Distinct.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; - $this->fieldName = (string) $fieldName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; + $this->fieldName = $fieldName; $this->filter = $filter; $this->options = $options; } @@ -141,22 +130,13 @@ public function __construct($databaseName, $collectionName, $fieldName, $filter * Execute the operation. * * @see Executable::execute() - * @param Server $server - * @return mixed[] + * @return array * @throws UnexpectedValueException if the command response was malformed - * @throws UnsupportedException if collation or read concern is used and unsupported + * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { - throw UnsupportedException::collationNotSupported(); - } - - if (isset($this->options['readConcern']) && ! server_supports_feature($server, self::$wireVersionForReadConcern)) { - throw UnsupportedException::readConcernNotSupported(); - } - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['readConcern'])) { throw UnsupportedException::readConcernNotSupportedInTransaction(); @@ -170,24 +150,35 @@ public function execute(Server $server) $result = current($cursor->toArray()); - if (! isset($result->values) || ! is_array($result->values)) { + if (! is_object($result) || ! isset($result->values) || ! is_array($result->values)) { throw new UnexpectedValueException('distinct command did not return a "values" array'); } return $result->values; } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->createCommandDocument(); + $cmd = $this->createCommandDocument(); + + // Read concern can change the query plan + if (isset($this->options['readConcern'])) { + $cmd['readConcern'] = $this->options['readConcern']; + } + + return $cmd; } /** * Create the distinct command document. - * - * @return array */ - private function createCommandDocument() + private function createCommandDocument(): array { $cmd = [ 'distinct' => $this->collectionName, @@ -202,8 +193,10 @@ private function createCommandDocument() $cmd['collation'] = (object) $this->options['collation']; } - if (isset($this->options['maxTimeMS'])) { - $cmd['maxTimeMS'] = $this->options['maxTimeMS']; + foreach (['comment', 'maxTimeMS'] as $option) { + if (isset($this->options[$option])) { + $cmd[$option] = $this->options[$option]; + } } return $cmd; @@ -212,10 +205,9 @@ private function createCommandDocument() /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executereadcommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; diff --git a/src/Operation/DropCollection.php b/src/Operation/DropCollection.php index 10f16e1ed..38b362306 100644 --- a/src/Operation/DropCollection.php +++ b/src/Operation/DropCollection.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->options = $options; } @@ -105,32 +97,24 @@ public function __construct($databaseName, $collectionName, array $options = []) * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return array|object Command result document - * @throws UnsupportedException if writeConcern is used and unsupported + * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) { - throw UnsupportedException::writeConcernNotSupported(); - } - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { throw UnsupportedException::writeConcernNotSupportedInTransaction(); } - $command = new Command(['drop' => $this->collectionName]); - try { - $cursor = $server->executeWriteCommand($this->databaseName, $command, $this->createOptions()); + $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); } catch (CommandException $e) { /* The server may return an error if the collection does not exist. - * Check for an error code (or message for pre-3.2 servers) and - * return the command reply instead of throwing. */ - if ($e->getCode() === self::$errorCodeNamespaceNotFound || - $e->getMessage() === self::$errorMessageNamespaceNotFound) { + * Check for an error code and return the command reply instead of + * throwing. */ + if ($e->getCode() === self::ERROR_CODE_NAMESPACE_NOT_FOUND) { return $e->getResultDocument(); } @@ -144,13 +128,26 @@ public function execute(Server $server) return current($cursor->toArray()); } + /** + * Create the drop command. + */ + private function createCommand(): Command + { + $cmd = ['drop' => $this->collectionName]; + + if (isset($this->options['comment'])) { + $cmd['comment'] = $this->options['comment']; + } + + return new Command($cmd); + } + /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executewritecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; diff --git a/src/Operation/DropDatabase.php b/src/Operation/DropDatabase.php index 5a4335a7d..e207c96de 100644 --- a/src/Operation/DropDatabase.php +++ b/src/Operation/DropDatabase.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; + $this->databaseName = $databaseName; $this->options = $options; } @@ -94,19 +88,12 @@ public function __construct($databaseName, array $options = []) * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return array|object Command result document - * @throws UnsupportedException if writeConcern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) { - throw UnsupportedException::writeConcernNotSupported(); - } - - $command = new Command(['dropDatabase' => 1]); - $cursor = $server->executeWriteCommand($this->databaseName, $command, $this->createOptions()); + $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); if (isset($this->options['typeMap'])) { $cursor->setTypeMap($this->options['typeMap']); @@ -115,13 +102,26 @@ public function execute(Server $server) return current($cursor->toArray()); } + /** + * Create the dropDatabase command. + */ + private function createCommand(): Command + { + $cmd = ['dropDatabase' => 1]; + + if (isset($this->options['comment'])) { + $cmd['comment'] = $this->options['comment']; + } + + return new Command($cmd); + } + /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executewritecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; diff --git a/src/Operation/DropEncryptedCollection.php b/src/Operation/DropEncryptedCollection.php new file mode 100644 index 000000000..027b14bfa --- /dev/null +++ b/src/Operation/DropEncryptedCollection.php @@ -0,0 +1,102 @@ +dropMetadataCollections = [ + new DropCollection($databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc'), + new DropCollection($databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc'), + ]; + + // DropCollection does not use the "encryptedFields" option + unset($options['encryptedFields']); + + $this->dropCollection = new DropCollection($databaseName, $collectionName, $options); + } + + /** + * @see Executable::execute() + * @return array|object Command result document from dropping the encrypted collection + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function execute(Server $server) + { + foreach ($this->dropMetadataCollections as $dropMetadataCollection) { + $dropMetadataCollection->execute($server); + } + + return $this->dropCollection->execute($server); + } +} diff --git a/src/Operation/DropIndexes.php b/src/Operation/DropIndexes.php index 322e3b10f..915fbe5a9 100644 --- a/src/Operation/DropIndexes.php +++ b/src/Operation/DropIndexes.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->indexName = $indexName; $this->options = $options; } @@ -117,17 +112,12 @@ public function __construct($databaseName, $collectionName, $indexName, array $o * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return array|object Command result document - * @throws UnsupportedException if writeConcern is used and unsupported + * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) { - throw UnsupportedException::writeConcernNotSupported(); - } - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { throw UnsupportedException::writeConcernNotSupportedInTransaction(); @@ -144,18 +134,18 @@ public function execute(Server $server) /** * Create the dropIndexes command. - * - * @return Command */ - private function createCommand() + private function createCommand(): Command { $cmd = [ 'dropIndexes' => $this->collectionName, 'index' => $this->indexName, ]; - if (isset($this->options['maxTimeMS'])) { - $cmd['maxTimeMS'] = $this->options['maxTimeMS']; + foreach (['comment', 'maxTimeMS'] as $option) { + if (isset($this->options[$option])) { + $cmd[$option] = $this->options[$option]; + } } return new Command($cmd); @@ -164,10 +154,9 @@ private function createCommand() /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executewritecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; diff --git a/src/Operation/EstimatedDocumentCount.php b/src/Operation/EstimatedDocumentCount.php index 2e6dd5ca1..ca53ff029 100644 --- a/src/Operation/EstimatedDocumentCount.php +++ b/src/Operation/EstimatedDocumentCount.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; - $this->options = array_intersect_key($options, ['maxTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1]); + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; + + if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { + throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); + } + + if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { + throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); + } - $this->count = $this->createCount(); + if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { + throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); + } + + if (isset($options['session']) && ! $options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + } + + $this->options = array_intersect_key($options, ['comment' => 1, 'maxTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1]); } /** * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if collation or read concern is used and unsupported @@ -90,18 +105,21 @@ public function __construct($databaseName, $collectionName, array $options = []) */ public function execute(Server $server) { - return $this->count->execute($server); + return $this->createCount()->execute($server); } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->count->getCommandDocument($server); + return $this->createCount()->getCommandDocument(); } - /** - * @return Count - */ - private function createCount() + private function createCount(): Count { return new Count($this->databaseName, $this->collectionName, [], $this->options); } diff --git a/src/Operation/Executable.php b/src/Operation/Executable.php index eee1b5fdc..5bd8c68b3 100644 --- a/src/Operation/Executable.php +++ b/src/Operation/Executable.php @@ -1,12 +1,12 @@ options = $options; } + /** + * Execute the operation. + * + * @see Executable::execute() + * @return array|object + * @throws UnsupportedException if the server does not support explaining the operation + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ public function execute(Server $server) { - if ($this->explainable instanceof Distinct && ! server_supports_feature($server, self::$wireVersionForDistinct)) { + if ($this->explainable instanceof Aggregate && ! server_supports_feature($server, self::WIRE_VERSION_FOR_AGGREGATE)) { throw UnsupportedException::explainNotSupported(); } - if ($this->isFindAndModify($this->explainable) && ! server_supports_feature($server, self::$wireVersionForFindAndModify)) { - throw UnsupportedException::explainNotSupported(); - } + $cursor = $server->executeCommand($this->databaseName, $this->createCommand(), $this->createOptions()); - if ($this->explainable instanceof Aggregate && ! server_supports_feature($server, self::$wireVersionForAggregate)) { - throw UnsupportedException::explainNotSupported(); + if (isset($this->options['typeMap'])) { + $cursor->setTypeMap($this->options['typeMap']); } - $cmd = ['explain' => $this->explainable->getCommandDocument($server)]; - - if (isset($this->options['verbosity'])) { - $cmd['verbosity'] = $this->options['verbosity']; - } + return current($cursor->toArray()); + } - $cursor = $server->executeCommand($this->databaseName, new Command($cmd), $this->createOptions()); + /** + * Create the explain command. + */ + private function createCommand(): Command + { + $cmd = ['explain' => $this->explainable->getCommandDocument()]; - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); + foreach (['comment', 'verbosity'] as $option) { + if (isset($this->options[$option])) { + $cmd[$option] = $this->options[$option]; + } } - return current($cursor->toArray()); + return new Command($cmd); } /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; @@ -150,13 +157,4 @@ private function createOptions() return $options; } - - private function isFindAndModify($explainable) - { - if ($explainable instanceof FindAndModify || $explainable instanceof FindOneAndDelete || $explainable instanceof FindOneAndReplace || $explainable instanceof FindOneAndUpdate) { - return true; - } - - return false; - } } diff --git a/src/Operation/Explainable.php b/src/Operation/Explainable.php index fb531f239..f380e1c32 100644 --- a/src/Operation/Explainable.php +++ b/src/Operation/Explainable.php @@ -1,12 +1,12 @@ isDefault()) { unset($options['readConcern']); } @@ -300,8 +292,8 @@ public function __construct($databaseName, $collectionName, $filter, array $opti trigger_error('The "maxScan" option is deprecated and will be removed in a future release', E_USER_DEPRECATED); } - $this->databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->filter = $filter; $this->options = $options; } @@ -310,25 +302,12 @@ public function __construct($databaseName, $collectionName, $filter, array $opti * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return Cursor - * @throws UnsupportedException if collation or read concern is used and unsupported + * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { - throw UnsupportedException::collationNotSupported(); - } - - if (isset($this->options['readConcern']) && ! server_supports_feature($server, self::$wireVersionForReadConcern)) { - throw UnsupportedException::readConcernNotSupported(); - } - - if (isset($this->options['allowDiskUse']) && ! server_supports_feature($server, self::$wireVersionForAllowDiskUseServerSideError)) { - throw UnsupportedException::allowDiskUseNotSupported(); - } - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['readConcern'])) { throw UnsupportedException::readConcernNotSupportedInTransaction(); @@ -343,15 +322,13 @@ public function execute(Server $server) return $cursor; } - public function getCommandDocument(Server $server) - { - return $this->createCommandDocument(); - } - /** - * Construct a command document for Find + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array */ - private function createCommandDocument() + public function getCommandDocument() { $cmd = ['find' => $this->collectionName, 'filter' => (object) $this->filter]; @@ -383,6 +360,7 @@ private function createCommandDocument() $options[$modifier[0]] = $options['modifiers'][$modifier[1]]; } } + unset($options['modifiers']); return $cmd + $options; @@ -391,10 +369,9 @@ private function createCommandDocument() /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executequery.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executequery.php */ - private function createExecuteOptions() + private function createExecuteOptions(): array { $options = []; @@ -414,10 +391,8 @@ private function createExecuteOptions() * * Note that these are separate from the options for executing the command, * which are created in createExecuteOptions(). - * - * @return array */ - private function createQueryOptions() + private function createQueryOptions(): array { $options = []; @@ -425,6 +400,7 @@ private function createQueryOptions() if ($this->options['cursorType'] === self::TAILABLE) { $options['tailable'] = true; } + if ($this->options['cursorType'] === self::TAILABLE_AWAIT) { $options['tailable'] = true; $options['awaitData'] = true; @@ -437,16 +413,16 @@ private function createQueryOptions() } } - foreach (['collation', 'max', 'min'] as $option) { + foreach (['collation', 'let', 'max', 'min'] as $option) { if (isset($this->options[$option])) { $options[$option] = (object) $this->options[$option]; } } - $modifiers = empty($this->options['modifiers']) ? [] : (array) $this->options['modifiers']; - - if (! empty($modifiers)) { - $options['modifiers'] = $modifiers; + if (! empty($this->options['modifiers'])) { + /** @psalm-var array|object */ + $modifiers = $this->options['modifiers']; + $options['modifiers'] = is_object($modifiers) ? document_to_array($modifiers) : $modifiers; } return $options; diff --git a/src/Operation/FindAndModify.php b/src/Operation/FindAndModify.php index 9e1e5949d..b8f9ce99a 100644 --- a/src/Operation/FindAndModify.php +++ b/src/Operation/FindAndModify.php @@ -1,12 +1,12 @@ false, - 'remove' => false, - 'upsert' => false, - ]; + $options += ['remove' => false]; if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) { throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array'); @@ -175,7 +155,7 @@ public function __construct($databaseName, $collectionName, array $options) throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); } - if (! is_bool($options['new'])) { + if (array_key_exists('new', $options) && ! is_bool($options['new'])) { throw InvalidArgumentException::invalidType('"new" option', $options['new'], 'boolean'); } @@ -207,10 +187,18 @@ public function __construct($databaseName, $collectionName, array $options) throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); } - if (! is_bool($options['upsert'])) { + if (array_key_exists('upsert', $options) && ! is_bool($options['upsert'])) { throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean'); } + if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { + throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object'); + } + + if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { + unset($options['bypassDocumentValidation']); + } + if (! (isset($options['update']) xor $options['remove'])) { throw new InvalidArgumentException('The "remove" option must be true or an "update" document must be specified, but not both'); } @@ -219,8 +207,8 @@ public function __construct($databaseName, $collectionName, array $options) unset($options['writeConcern']); } - $this->databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->options = $options; } @@ -228,32 +216,26 @@ public function __construct($databaseName, $collectionName, array $options) * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return array|object|null * @throws UnexpectedValueException if the command response was malformed - * @throws UnsupportedException if array filters, collation, or write concern is used and unsupported + * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['arrayFilters']) && ! server_supports_feature($server, self::$wireVersionForArrayFilters)) { - throw UnsupportedException::arrayFiltersNotSupported(); - } - - if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { - throw UnsupportedException::collationNotSupported(); - } - - /* Server versions >= 4.1.10 raise errors for unknown findAndModify - * options (SERVER-40005), but the CRUD spec requires client-side errors - * for server versions < 4.2. For later versions, we'll rely on the - * server to either utilize the option or report its own error. */ - if (isset($this->options['hint']) && ! $this->isHintSupported($server)) { + /* Server versions >= 4.2.0 raise errors for unsupported update options. + * For previous versions, the CRUD spec requires a client-side error. */ + if (isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_UNSUPPORTED_OPTION_SERVER_SIDE_ERROR)) { throw UnsupportedException::hintNotSupported(); } - if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) { - throw UnsupportedException::writeConcernNotSupported(); + /* CRUD spec requires a client-side error when using "hint" with an + * unacknowledged write concern on an unsupported server. */ + if ( + isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) && + isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_HINT) + ) { + throw UnsupportedException::hintNotSupported(); } $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); @@ -261,7 +243,7 @@ public function execute(Server $server) throw UnsupportedException::writeConcernNotSupportedInTransaction(); } - $cursor = $server->executeWriteCommand($this->databaseName, new Command($this->createCommandDocument($server)), $this->createOptions()); + $cursor = $server->executeWriteCommand($this->databaseName, new Command($this->createCommandDocument()), $this->createOptions()); if (isset($this->options['typeMap'])) { $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'value')); @@ -269,65 +251,74 @@ public function execute(Server $server) $result = current($cursor->toArray()); - return $result->value ?? null; + return is_object($result) ? ($result->value ?? null) : null; } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->createCommandDocument($server); + return $this->createCommandDocument(); } /** * Create the findAndModify command document. - * - * @param Server $server - * @return array */ - private function createCommandDocument(Server $server) + private function createCommandDocument(): array { $cmd = ['findAndModify' => $this->collectionName]; if ($this->options['remove']) { $cmd['remove'] = true; } else { - $cmd['new'] = $this->options['new']; - $cmd['upsert'] = $this->options['upsert']; + if (isset($this->options['new'])) { + $cmd['new'] = $this->options['new']; + } + + if (isset($this->options['upsert'])) { + $cmd['upsert'] = $this->options['upsert']; + } } - foreach (['collation', 'fields', 'query', 'sort'] as $option) { + foreach (['collation', 'fields', 'let', 'query', 'sort'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = (object) $this->options[$option]; } } if (isset($this->options['update'])) { - $cmd['update'] = is_pipeline($this->options['update']) - ? $this->options['update'] - : (object) $this->options['update']; + /** @psalm-var array|object */ + $update = $this->options['update']; + /* A non-empty pipeline will encode as a BSON array, so leave it + * as-is. Cast anything else to an object since a BSON document is + * likely expected. This includes empty arrays, which historically + * can be used to represent empty replacement documents. + * + * This also allows an empty pipeline expressed as a PackedArray or + * Serializable to still encode as a BSON array, since the object + * cast will have no effect. */ + $cmd['update'] = is_pipeline($update) ? $update : (object) $update; } - foreach (['arrayFilters', 'hint', 'maxTimeMS'] as $option) { + foreach (['arrayFilters', 'bypassDocumentValidation', 'comment', 'hint', 'maxTimeMS'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = $this->options[$option]; } } - if (! empty($this->options['bypassDocumentValidation']) && - server_supports_feature($server, self::$wireVersionForDocumentLevelValidation) - ) { - $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; - } - return $cmd; } /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executewritecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; @@ -341,20 +332,4 @@ private function createOptions() return $options; } - - private function isAcknowledgedWriteConcern() : bool - { - if (! isset($this->options['writeConcern'])) { - return true; - } - - return $this->options['writeConcern']->getW() > 1 || $this->options['writeConcern']->getJournal(); - } - - private function isHintSupported(Server $server) : bool - { - $requiredWireVersion = $this->isAcknowledgedWriteConcern() ? self::$wireVersionForHintServerSideError : self::$wireVersionForHint; - - return server_supports_feature($server, $requiredWireVersion); - } } diff --git a/src/Operation/FindOne.php b/src/Operation/FindOne.php index 48f601ce6..095ffaf1a 100644 --- a/src/Operation/FindOne.php +++ b/src/Operation/FindOne.php @@ -1,12 +1,12 @@ find = new Find( $databaseName, @@ -118,7 +116,6 @@ public function __construct($databaseName, $collectionName, $filter, array $opti * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return array|object|null * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -131,8 +128,14 @@ public function execute(Server $server) return $document === false ? null : $document; } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->find->getCommandDocument($server); + return $this->find->getCommandDocument(); } } diff --git a/src/Operation/FindOneAndDelete.php b/src/Operation/FindOneAndDelete.php index 88ed57f7e..362d04346 100644 --- a/src/Operation/FindOneAndDelete.php +++ b/src/Operation/FindOneAndDelete.php @@ -1,12 +1,12 @@ findAndModify->execute($server); } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->findAndModify->getCommandDocument($server); + return $this->findAndModify->getCommandDocument(); } } diff --git a/src/Operation/FindOneAndReplace.php b/src/Operation/FindOneAndReplace.php index 61256f518..5ec774e47 100644 --- a/src/Operation/FindOneAndReplace.php +++ b/src/Operation/FindOneAndReplace.php @@ -1,12 +1,12 @@ self::RETURN_DOCUMENT_BEFORE, - 'upsert' => false, - ]; + if (is_pipeline($replacement, true /* allowEmpty */)) { + throw new InvalidArgumentException('$replacement is an update pipeline'); + } if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) { throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object'); } - if (! is_integer($options['returnDocument'])) { + if (array_key_exists('returnDocument', $options) && ! is_integer($options['returnDocument'])) { throw InvalidArgumentException::invalidType('"returnDocument" option', $options['returnDocument'], 'integer'); } - if ($options['returnDocument'] !== self::RETURN_DOCUMENT_AFTER && - $options['returnDocument'] !== self::RETURN_DOCUMENT_BEFORE) { + if ( + isset($options['returnDocument']) && + $options['returnDocument'] !== self::RETURN_DOCUMENT_AFTER && + $options['returnDocument'] !== self::RETURN_DOCUMENT_BEFORE + ) { throw new InvalidArgumentException('Invalid value for "returnDocument" option: ' . $options['returnDocument']); } @@ -136,7 +143,9 @@ public function __construct($databaseName, $collectionName, $filter, $replacemen $options['fields'] = $options['projection']; } - $options['new'] = $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER; + if (isset($options['returnDocument'])) { + $options['new'] = $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER; + } unset($options['projection'], $options['returnDocument']); @@ -151,7 +160,6 @@ public function __construct($databaseName, $collectionName, $filter, $replacemen * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return array|object|null * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -161,8 +169,14 @@ public function execute(Server $server) return $this->findAndModify->execute($server); } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->findAndModify->getCommandDocument($server); + return $this->findAndModify->getCommandDocument(); } } diff --git a/src/Operation/FindOneAndUpdate.php b/src/Operation/FindOneAndUpdate.php index c8121a9b5..5df15076a 100644 --- a/src/Operation/FindOneAndUpdate.php +++ b/src/Operation/FindOneAndUpdate.php @@ -1,12 +1,12 @@ self::RETURN_DOCUMENT_BEFORE, - 'upsert' => false, - ]; - if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) { throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object'); } - if (! is_integer($options['returnDocument'])) { + if (array_key_exists('returnDocument', $options) && ! is_integer($options['returnDocument'])) { throw InvalidArgumentException::invalidType('"returnDocument" option', $options['returnDocument'], 'integer'); } - if ($options['returnDocument'] !== self::RETURN_DOCUMENT_AFTER && - $options['returnDocument'] !== self::RETURN_DOCUMENT_BEFORE) { + if ( + isset($options['returnDocument']) && + $options['returnDocument'] !== self::RETURN_DOCUMENT_AFTER && + $options['returnDocument'] !== self::RETURN_DOCUMENT_BEFORE + ) { throw new InvalidArgumentException('Invalid value for "returnDocument" option: ' . $options['returnDocument']); } @@ -140,7 +137,9 @@ public function __construct($databaseName, $collectionName, $filter, $update, ar $options['fields'] = $options['projection']; } - $options['new'] = $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER; + if (isset($options['returnDocument'])) { + $options['new'] = $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER; + } unset($options['projection'], $options['returnDocument']); @@ -155,7 +154,6 @@ public function __construct($databaseName, $collectionName, $filter, $update, ar * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return array|object|null * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -165,8 +163,14 @@ public function execute(Server $server) return $this->findAndModify->execute($server); } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->findAndModify->getCommandDocument($server); + return $this->findAndModify->getCommandDocument(); } } diff --git a/src/Operation/InsertMany.php b/src/Operation/InsertMany.php index 768907592..a257d069c 100644 --- a/src/Operation/InsertMany.php +++ b/src/Operation/InsertMany.php @@ -1,12 +1,12 @@ $document) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$documents is not a list (unexpected index: "%s")', $i)); - } - if (! is_array($document) && ! is_object($document)) { throw InvalidArgumentException::invalidType(sprintf('$documents[%d]', $i), $document, 'array or object'); } - - $expectedIndex += 1; } $options += ['ordered' => true]; @@ -120,12 +113,16 @@ public function __construct($databaseName, $collectionName, array $documents, ar throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); } + if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { + unset($options['bypassDocumentValidation']); + } + if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { unset($options['writeConcern']); } - $this->databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->documents = $documents; $this->options = $options; } @@ -134,8 +131,8 @@ public function __construct($databaseName, $collectionName, array $documents, ar * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return InsertManyResult + * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) @@ -145,33 +142,42 @@ public function execute(Server $server) throw UnsupportedException::writeConcernNotSupportedInTransaction(); } - $options = ['ordered' => $this->options['ordered']]; - - if (! empty($this->options['bypassDocumentValidation']) && - server_supports_feature($server, self::$wireVersionForDocumentLevelValidation) - ) { - $options['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; - } - - $bulk = new Bulk($options); + $bulk = new Bulk($this->createBulkWriteOptions()); $insertedIds = []; foreach ($this->documents as $i => $document) { $insertedIds[$i] = $bulk->insert($document); } - $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createOptions()); + $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); return new InsertManyResult($writeResult, $insertedIds); } + /** + * Create options for constructing the bulk write. + * + * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php + */ + private function createBulkWriteOptions(): array + { + $options = ['ordered' => $this->options['ordered']]; + + foreach (['bypassDocumentValidation', 'comment'] as $option) { + if (isset($this->options[$option])) { + $options[$option] = $this->options[$option]; + } + } + + return $options; + } + /** * Create options for executing the bulk write. * - * @see http://php.net/manual/en/mongodb-driver-server.executebulkwrite.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php */ - private function createOptions() + private function createExecuteOptions(): array { $options = []; diff --git a/src/Operation/InsertOne.php b/src/Operation/InsertOne.php index 84d6a4aa1..0d32530e6 100644 --- a/src/Operation/InsertOne.php +++ b/src/Operation/InsertOne.php @@ -1,12 +1,12 @@ isDefault()) { unset($options['writeConcern']); } - $this->databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->document = $document; $this->options = $options; } @@ -109,40 +108,49 @@ public function __construct($databaseName, $collectionName, $document, array $op * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return InsertOneResult + * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - $options = []; - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if (isset($this->options['writeConcern']) && $inTransaction) { throw UnsupportedException::writeConcernNotSupportedInTransaction(); } - if (! empty($this->options['bypassDocumentValidation']) && - server_supports_feature($server, self::$wireVersionForDocumentLevelValidation) - ) { - $options['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; - } - - $bulk = new Bulk($options); + $bulk = new Bulk($this->createBulkWriteOptions()); $insertedId = $bulk->insert($this->document); - $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createOptions()); + $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); return new InsertOneResult($writeResult, $insertedId); } + /** + * Create options for constructing the bulk write. + * + * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php + */ + private function createBulkWriteOptions(): array + { + $options = []; + + foreach (['bypassDocumentValidation', 'comment'] as $option) { + if (isset($this->options[$option])) { + $options[$option] = $this->options[$option]; + } + } + + return $options; + } + /** * Create options for executing the bulk write. * - * @see http://php.net/manual/en/mongodb-driver-server.executebulkwrite.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php */ - private function createOptions() + private function createExecuteOptions(): array { $options = []; diff --git a/src/Operation/ListCollectionNames.php b/src/Operation/ListCollectionNames.php index e36bf5688..ebfa5977b 100644 --- a/src/Operation/ListCollectionNames.php +++ b/src/Operation/ListCollectionNames.php @@ -6,7 +6,7 @@ * 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 + * https://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, @@ -27,9 +27,8 @@ /** * Operation for the listCollectionNames helper. * - * @api * @see \MongoDB\Database::listCollectionNames() - * @see http://docs.mongodb.org/manual/reference/command/listCollections/ + * @see https://mongodb.com/docs/manual/reference/command/listCollections/ */ class ListCollectionNames implements Executable { @@ -41,6 +40,15 @@ class ListCollectionNames implements Executable * * Supported options: * + * * authorizedCollections (boolean): Determines which collections are + * returned based on the user privileges. + * + * For servers < 4.0, this option is ignored. + * + * * comment (mixed): BSON value to attach as a comment to this command. + * + * This is not supported for servers versions < 4.4. + * * * filter (document): Query by which to filter collections. * * * maxTimeMS (integer): The maximum amount of time to allow the query to @@ -48,13 +56,11 @@ class ListCollectionNames implements Executable * * * session (MongoDB\Driver\Session): Client session. * - * Sessions are not supported for server versions < 3.6. - * * @param string $databaseName Database name * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct($databaseName, array $options = []) + public function __construct(string $databaseName, array $options = []) { $this->listCollections = new ListCollectionsCommand($databaseName, ['nameOnly' => true] + $options); } @@ -63,11 +69,10 @@ public function __construct($databaseName, array $options = []) * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return Iterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) : Iterator + public function execute(Server $server): Iterator { return new CallbackIterator( $this->listCollections->execute($server), diff --git a/src/Operation/ListCollections.php b/src/Operation/ListCollections.php index 838202cbb..a46968983 100644 --- a/src/Operation/ListCollections.php +++ b/src/Operation/ListCollections.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; + $this->databaseName = $databaseName; $this->listCollections = new ListCollectionsCommand($databaseName, ['nameOnly' => false] + $options); } @@ -67,7 +73,6 @@ public function __construct($databaseName, array $options = []) * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return CollectionInfoIterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/ListDatabaseNames.php b/src/Operation/ListDatabaseNames.php index a9d5a8a87..db2a468cd 100644 --- a/src/Operation/ListDatabaseNames.php +++ b/src/Operation/ListDatabaseNames.php @@ -6,7 +6,7 @@ * 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 + * https://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, @@ -24,14 +24,14 @@ use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; + use function array_column; /** * Operation for the ListDatabases command, returning only database names. * - * @api * @see \MongoDB\Client::listDatabaseNames() - * @see http://docs.mongodb.org/manual/reference/command/ListDatabases/ + * @see https://mongodb.com/docs/manual/reference/command/listDatabases/#mongodb-dbcommand-dbcmd.listDatabases */ class ListDatabaseNames implements Executable { @@ -48,17 +48,17 @@ class ListDatabaseNames implements Executable * * For servers < 4.0.5, this option is ignored. * - * * filter (document): Query by which to filter databases. + * * comment (mixed): BSON value to attach as a comment to this command. * - * For servers < 3.6, this option is ignored. + * This is not supported for servers versions < 4.4. + * + * * filter (document): Query by which to filter databases. * * * maxTimeMS (integer): The maximum amount of time to allow the query to * run. * * * session (MongoDB\Driver\Session): Client session. * - * Sessions are not supported for server versions < 3.6. - * * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ @@ -71,12 +71,10 @@ public function __construct(array $options = []) * Execute the operation. * * @see Executable::execute() - * @param Server $server - * @return Iterator * @throws UnexpectedValueException if the command response was malformed * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) : Iterator + public function execute(Server $server): Iterator { $result = $this->listDatabases->execute($server); diff --git a/src/Operation/ListDatabases.php b/src/Operation/ListDatabases.php index 66542966f..412538c16 100644 --- a/src/Operation/ListDatabases.php +++ b/src/Operation/ListDatabases.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->options = $options; } @@ -88,7 +88,6 @@ public function __construct($databaseName, $collectionName, array $options = []) * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return IndexInfoIterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ @@ -103,10 +102,9 @@ public function execute(Server $server) * Note: read preference is intentionally omitted, as the spec requires that * the command be executed on the primary. * - * @see http://php.net/manual/en/mongodb-driver-server.executecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; @@ -121,26 +119,26 @@ private function createOptions() * Returns information for all indexes for this collection using the * listIndexes command. * - * @param Server $server - * @return IndexInfoIteratorIterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - private function executeCommand(Server $server) + private function executeCommand(Server $server): IndexInfoIteratorIterator { $cmd = ['listIndexes' => $this->collectionName]; - if (isset($this->options['maxTimeMS'])) { - $cmd['maxTimeMS'] = $this->options['maxTimeMS']; + foreach (['comment', 'maxTimeMS'] as $option) { + if (isset($this->options[$option])) { + $cmd[$option] = $this->options[$option]; + } } try { $cursor = $server->executeReadCommand($this->databaseName, new Command($cmd), $this->createOptions()); - } catch (DriverRuntimeException $e) { + } catch (CommandException $e) { /* The server may return an error if the collection does not exist. * Check for possible error codes (see: SERVER-20463) and return an * empty iterator instead of throwing. */ - if ($e->getCode() === self::$errorCodeNamespaceNotFound || $e->getCode() === self::$errorCodeDatabaseNotFound) { + if ($e->getCode() === self::ERROR_CODE_NAMESPACE_NOT_FOUND || $e->getCode() === self::ERROR_CODE_DATABASE_NOT_FOUND) { return new IndexInfoIteratorIterator(new EmptyIterator()); } diff --git a/src/Operation/MapReduce.php b/src/Operation/MapReduce.php index c1bf6926a..58d11938f 100644 --- a/src/Operation/MapReduce.php +++ b/src/Operation/MapReduce.php @@ -1,12 +1,12 @@ isDefault()) { unset($options['readConcern']); } @@ -268,8 +252,8 @@ public function __construct($databaseName, $collectionName, JavascriptInterface $this->checkOutDeprecations($out); - $this->databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->map = $map; $this->reduce = $reduce; $this->out = $out; @@ -280,31 +264,19 @@ public function __construct($databaseName, $collectionName, JavascriptInterface * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return MapReduceResult * @throws UnexpectedValueException if the command response was malformed - * @throws UnsupportedException if collation, read concern, or write concern is used and unsupported + * @throws UnsupportedException if read concern or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { - throw UnsupportedException::collationNotSupported(); - } - - if (isset($this->options['readConcern']) && ! server_supports_feature($server, self::$wireVersionForReadConcern)) { - throw UnsupportedException::readConcernNotSupported(); - } - - if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) { - throw UnsupportedException::writeConcernNotSupported(); - } - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction) { if (isset($this->options['readConcern'])) { throw UnsupportedException::readConcernNotSupportedInTransaction(); } + if (isset($this->options['writeConcern'])) { throw UnsupportedException::writeConcernNotSupportedInTransaction(); } @@ -312,7 +284,7 @@ public function execute(Server $server) $hasOutputCollection = ! is_mapreduce_output_inline($this->out); - $command = $this->createCommand($server); + $command = $this->createCommand(); $options = $this->createOptions($hasOutputCollection); /* If the mapReduce operation results in a write, use @@ -330,23 +302,21 @@ public function execute(Server $server) } $result = current($cursor->toArray()); + assert($result instanceof stdClass); $getIterator = $this->createGetIteratorCallable($result, $server); return new MapReduceResult($getIterator, $result); } - /** - * @param string|array|object $out - * @return void - */ - private function checkOutDeprecations($out) + /** @param string|array|object $out */ + private function checkOutDeprecations($out): void { if (is_string($out)) { return; } - $out = (array) $out; + $out = document_to_array($out); if (isset($out['nonAtomic']) && ! $out['nonAtomic']) { @trigger_error('Specifying false for "out.nonAtomic" is deprecated.', E_USER_DEPRECATED); @@ -359,11 +329,8 @@ private function checkOutDeprecations($out) /** * Create the mapReduce command. - * - * @param Server $server - * @return Command */ - private function createCommand(Server $server) + private function createCommand(): Command { $cmd = [ 'mapReduce' => $this->collectionName, @@ -372,7 +339,7 @@ private function createCommand(Server $server) 'out' => $this->out, ]; - foreach (['finalize', 'jsMode', 'limit', 'maxTimeMS', 'verbose'] as $option) { + foreach (['bypassDocumentValidation', 'comment', 'finalize', 'jsMode', 'limit', 'maxTimeMS', 'verbose'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = $this->options[$option]; } @@ -384,24 +351,15 @@ private function createCommand(Server $server) } } - if (! empty($this->options['bypassDocumentValidation']) && - server_supports_feature($server, self::$wireVersionForDocumentLevelValidation) - ) { - $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; - } - return new Command($cmd); } /** * Creates a callable for MapReduceResult::getIterator(). * - * @param stdClass $result - * @param Server $server - * @return callable * @throws UnexpectedValueException if the command response was malformed */ - private function createGetIteratorCallable(stdClass $result, Server $server) + private function createGetIteratorCallable(stdClass $result, Server $server): callable { // Inline results can be wrapped with an ArrayIterator if (isset($result->results) && is_array($result->results)) { @@ -430,12 +388,10 @@ private function createGetIteratorCallable(stdClass $result, Server $server) /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executereadcommand.php - * @see http://php.net/manual/en/mongodb-driver-server.executereadwritecommand.php - * @param boolean $hasOutputCollection - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php + * @see https://php.net/manual/en/mongodb-driver-server.executereadwritecommand.php */ - private function createOptions($hasOutputCollection) + private function createOptions(bool $hasOutputCollection): array { $options = []; diff --git a/src/Operation/ModifyCollection.php b/src/Operation/ModifyCollection.php index 3b5177fd5..61b5aafe6 100644 --- a/src/Operation/ModifyCollection.php +++ b/src/Operation/ModifyCollection.php @@ -1,12 +1,12 @@ databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->collectionOptions = $collectionOptions; $this->options = $options; } @@ -104,17 +101,12 @@ public function __construct($databaseName, $collectionName, array $collectionOpt * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return array|object Command result document * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) { - throw UnsupportedException::writeConcernNotSupported(); - } - - $cursor = $server->executeWriteCommand($this->databaseName, new Command(['collMod' => $this->collectionName] + $this->collectionOptions), $this->createOptions()); + $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); if (isset($this->options['typeMap'])) { $cursor->setTypeMap($this->options['typeMap']); @@ -123,13 +115,23 @@ public function execute(Server $server) return current($cursor->toArray()); } + private function createCommand(): Command + { + $cmd = ['collMod' => $this->collectionName] + $this->collectionOptions; + + if (isset($this->options['comment'])) { + $cmd['comment'] = $this->options['comment']; + } + + return new Command($cmd); + } + /** * Create options for executing the command. * - * @see http://php.net/manual/en/mongodb-driver-server.executewritecommand.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php */ - private function createOptions() + private function createOptions(): array { $options = []; diff --git a/src/Operation/RenameCollection.php b/src/Operation/RenameCollection.php new file mode 100644 index 000000000..5ab7a6f20 --- /dev/null +++ b/src/Operation/RenameCollection.php @@ -0,0 +1,165 @@ +isDefault()) { + unset($options['writeConcern']); + } + + if (isset($options['dropTarget']) && ! is_bool($options['dropTarget'])) { + throw InvalidArgumentException::invalidType('"dropTarget" option', $options['dropTarget'], 'boolean'); + } + + $this->fromNamespace = $fromDatabaseName . '.' . $fromCollectionName; + $this->toNamespace = $toDatabaseName . '.' . $toCollectionName; + $this->options = $options; + } + + /** + * Execute the operation. + * + * @see Executable::execute() + * @return array|object Command result document + * @throws UnsupportedException if write concern is used and unsupported + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function execute(Server $server) + { + $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); + if ($inTransaction && isset($this->options['writeConcern'])) { + throw UnsupportedException::writeConcernNotSupportedInTransaction(); + } + + $cursor = $server->executeWriteCommand('admin', $this->createCommand(), $this->createOptions()); + + if (isset($this->options['typeMap'])) { + $cursor->setTypeMap($this->options['typeMap']); + } + + return current($cursor->toArray()); + } + + /** + * Create the renameCollection command. + */ + private function createCommand(): Command + { + $cmd = [ + 'renameCollection' => $this->fromNamespace, + 'to' => $this->toNamespace, + ]; + + foreach (['comment', 'dropTarget'] as $option) { + if (isset($this->options[$option])) { + $cmd[$option] = $this->options[$option]; + } + } + + return new Command($cmd); + } + + /** + * Create options for executing the command. + * + * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php + */ + private function createOptions(): array + { + $options = []; + + if (isset($this->options['session'])) { + $options['session'] = $this->options['session']; + } + + if (isset($this->options['writeConcern'])) { + $options['writeConcern'] = $this->options['writeConcern']; + } + + return $options; + } +} diff --git a/src/Operation/ReplaceOne.php b/src/Operation/ReplaceOne.php index e55bf74af..a2b641aa8 100644 --- a/src/Operation/ReplaceOne.php +++ b/src/Operation/ReplaceOne.php @@ -1,12 +1,12 @@ update = new Update( @@ -105,7 +111,6 @@ public function __construct($databaseName, $collectionName, $filter, $replacemen * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return UpdateResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/Update.php b/src/Operation/Update.php index b3b86e0c6..64f71d4ed 100644 --- a/src/Operation/Update.php +++ b/src/Operation/Update.php @@ -1,12 +1,12 @@ isDefault()) { unset($options['writeConcern']); } - $this->databaseName = (string) $databaseName; - $this->collectionName = (string) $collectionName; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->filter = $filter; $this->update = $update; $this->options = $options; @@ -187,25 +185,18 @@ public function __construct($databaseName, $collectionName, $filter, $update, ar * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return UpdateResult - * @throws UnsupportedException if array filters or collation is used and unsupported + * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server) { - if (isset($this->options['arrayFilters']) && ! server_supports_feature($server, self::$wireVersionForArrayFilters)) { - throw UnsupportedException::arrayFiltersNotSupported(); - } - - if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { - throw UnsupportedException::collationNotSupported(); - } - - /* Server versions >= 3.4.0 raise errors for unknown update - * options. For previous versions, the CRUD spec requires a client-side - * error. */ - if (isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHintServerSideError)) { + /* CRUD spec requires a client-side error when using "hint" with an + * unacknowledged write concern on an unsupported server. */ + if ( + isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) && + isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_HINT) + ) { throw UnsupportedException::hintNotSupported(); } @@ -214,15 +205,7 @@ public function execute(Server $server) throw UnsupportedException::writeConcernNotSupportedInTransaction(); } - $bulkOptions = []; - - if (! empty($this->options['bypassDocumentValidation']) && - server_supports_feature($server, self::$wireVersionForDocumentLevelValidation) - ) { - $bulkOptions['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; - } - - $bulk = new Bulk($bulkOptions); + $bulk = new Bulk($this->createBulkWriteOptions()); $bulk->update($this->filter, $this->update, $this->createUpdateOptions()); $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); @@ -230,30 +213,51 @@ public function execute(Server $server) return new UpdateResult($writeResult); } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { $cmd = ['update' => $this->collectionName, 'updates' => [['q' => $this->filter, 'u' => $this->update] + $this->createUpdateOptions()]]; - if (isset($this->options['writeConcern'])) { - $cmd['writeConcern'] = $this->options['writeConcern']; - } - - if (! empty($this->options['bypassDocumentValidation']) && - server_supports_feature($server, self::$wireVersionForDocumentLevelValidation) - ) { + if (isset($this->options['bypassDocumentValidation'])) { $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; } return $cmd; } + /** + * Create options for constructing the bulk write. + * + * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php + */ + private function createBulkWriteOptions(): array + { + $options = []; + + foreach (['bypassDocumentValidation', 'comment'] as $option) { + if (isset($this->options[$option])) { + $options[$option] = $this->options[$option]; + } + } + + if (isset($this->options['let'])) { + $options['let'] = (object) $this->options['let']; + } + + return $options; + } + /** * Create options for executing the bulk write. * - * @see http://php.net/manual/en/mongodb-driver-server.executebulkwrite.php - * @return array + * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php */ - private function createExecuteOptions() + private function createExecuteOptions(): array { $options = []; @@ -273,10 +277,8 @@ private function createExecuteOptions() * * Note that these options are different from the bulk write options, which * are created in createExecuteOptions(). - * - * @return array */ - private function createUpdateOptions() + private function createUpdateOptions(): array { $updateOptions = [ 'multi' => $this->options['multi'], diff --git a/src/Operation/UpdateMany.php b/src/Operation/UpdateMany.php index eb1bd6cd5..b4c0062cd 100644 --- a/src/Operation/UpdateMany.php +++ b/src/Operation/UpdateMany.php @@ -1,12 +1,12 @@ update = new Update( @@ -107,7 +105,6 @@ public function __construct($databaseName, $collectionName, $filter, $update, ar * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return UpdateResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -117,8 +114,14 @@ public function execute(Server $server) return $this->update->execute($server); } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->update->getCommandDocument($server); + return $this->update->getCommandDocument(); } } diff --git a/src/Operation/UpdateOne.php b/src/Operation/UpdateOne.php index e8e7ef842..581acea35 100644 --- a/src/Operation/UpdateOne.php +++ b/src/Operation/UpdateOne.php @@ -1,12 +1,12 @@ update = new Update( @@ -107,7 +105,6 @@ public function __construct($databaseName, $collectionName, $filter, $update, ar * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return UpdateResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -117,8 +114,14 @@ public function execute(Server $server) return $this->update->execute($server); } - public function getCommandDocument(Server $server) + /** + * Returns the command document for this operation. + * + * @see Explainable::getCommandDocument() + * @return array + */ + public function getCommandDocument() { - return $this->update->getCommandDocument($server); + return $this->update->getCommandDocument(); } } diff --git a/src/Operation/Watch.php b/src/Operation/Watch.php index 1c88b87be..df230a72f 100644 --- a/src/Operation/Watch.php +++ b/src/Operation/Watch.php @@ -1,12 +1,12 @@ self::FULL_DOCUMENT_DEFAULT, - 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), ]; - if (! is_string($options['fullDocument'])) { + if (array_key_exists('fullDocument', $options) && ! is_string($options['fullDocument'])) { throw InvalidArgumentException::invalidType('"fullDocument" option', $options['fullDocument'], 'string'); } + if (isset($options['fullDocumentBeforeChange']) && ! is_string($options['fullDocumentBeforeChange'])) { + throw InvalidArgumentException::invalidType('"fullDocumentBeforeChange" option', $options['fullDocumentBeforeChange'], 'string'); + } + if (! $options['readPreference'] instanceof ReadPreference) { throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); } @@ -198,6 +234,10 @@ public function __construct(Manager $manager, $databaseName, $collectionName, ar throw InvalidArgumentException::invalidType('"startAtOperationTime" option', $options['startAtOperationTime'], TimestampInterface::class); } + if (isset($options['showExpandedEvents']) && ! is_bool($options['showExpandedEvents'])) { + throw InvalidArgumentException::invalidType('"showExpandedEvents" option', $options['showExpandedEvents'], 'bool'); + } + /* In the absence of an explicit session, create one to ensure that the * initial aggregation and any resume attempts can use the same session * ("implicit from the user's perspective" per PHPLIB-342). Since this @@ -212,8 +252,8 @@ public function __construct(Manager $manager, $databaseName, $collectionName, ar } } - $this->aggregateOptions = array_intersect_key($options, ['batchSize' => 1, 'collation' => 1, 'maxAwaitTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1, 'typeMap' => 1]); - $this->changeStreamOptions = array_intersect_key($options, ['fullDocument' => 1, 'resumeAfter' => 1, 'startAfter' => 1, 'startAtOperationTime' => 1]); + $this->aggregateOptions = array_intersect_key($options, ['batchSize' => 1, 'collation' => 1, 'comment' => 1, 'maxAwaitTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1, 'typeMap' => 1]); + $this->changeStreamOptions = array_intersect_key($options, ['fullDocument' => 1, 'fullDocumentBeforeChange' => 1, 'resumeAfter' => 1, 'showExpandedEvents' => 1, 'startAfter' => 1, 'startAtOperationTime' => 1]); // Null database name implies a cluster-wide change stream if ($databaseName === null) { @@ -222,31 +262,31 @@ public function __construct(Manager $manager, $databaseName, $collectionName, ar } $this->manager = $manager; - $this->databaseName = (string) $databaseName; - $this->collectionName = isset($collectionName) ? (string) $collectionName : null; + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; $this->pipeline = $pipeline; $this->aggregate = $this->createAggregate(); } /** @internal */ - final public function commandFailed(CommandFailedEvent $event) + final public function commandFailed(CommandFailedEvent $event): void { } /** @internal */ - final public function commandStarted(CommandStartedEvent $event) + final public function commandStarted(CommandStartedEvent $event): void { if ($event->getCommandName() !== 'aggregate') { return; } - $this->firstBatchSize = null; + $this->firstBatchSize = 0; $this->postBatchResumeToken = null; } /** @internal */ - final public function commandSucceeded(CommandSucceededEvent $event) + final public function commandSucceeded(CommandSucceededEvent $event): void { if ($event->getCommandName() !== 'aggregate') { return; @@ -264,8 +304,10 @@ final public function commandSucceeded(CommandSucceededEvent $event) $this->postBatchResumeToken = $reply->cursor->postBatchResumeToken; } - if ($this->shouldCaptureOperationTime($event->getServer()) && - isset($reply->operationTime) && $reply->operationTime instanceof TimestampInterface) { + if ( + $this->shouldCaptureOperationTime($event->getServer()) && + isset($reply->operationTime) && $reply->operationTime instanceof TimestampInterface + ) { $this->operationTime = $reply->operationTime; } } @@ -274,7 +316,6 @@ final public function commandSucceeded(CommandSucceededEvent $event) * Execute the operation. * * @see Executable::execute() - * @param Server $server * @return ChangeStream * @throws UnsupportedException if collation or read concern is used and unsupported * @throws RuntimeException for other driver errors (e.g. connection errors) @@ -283,7 +324,7 @@ public function execute(Server $server) { return new ChangeStream( $this->createChangeStreamIterator($server), - function ($resumeToken, $hasAdvanced) { + function ($resumeToken, $hasAdvanced): ChangeStreamIterator { return $this->resume($resumeToken, $hasAdvanced); } ); @@ -293,10 +334,8 @@ function ($resumeToken, $hasAdvanced) { * Create the aggregate command for a change stream. * * This method is also used to recreate the aggregate command when resuming. - * - * @return Aggregate */ - private function createAggregate() + private function createAggregate(): Aggregate { $pipeline = $this->pipeline; array_unshift($pipeline, ['$changeStream' => (object) $this->changeStreamOptions]); @@ -306,11 +345,8 @@ private function createAggregate() /** * Create a ChangeStreamIterator by executing the aggregate command. - * - * @param Server $server - * @return ChangeStreamIterator */ - private function createChangeStreamIterator(Server $server) + private function createChangeStreamIterator(Server $server): ChangeStreamIterator { return new ChangeStreamIterator( $this->executeAggregate($server), @@ -325,16 +361,16 @@ private function createChangeStreamIterator(Server $server) * * The command will be executed using APM so that we can capture data from * its response (e.g. firstBatch size, postBatchResumeToken). - * - * @param Server $server - * @return Cursor */ - private function executeAggregate(Server $server) + private function executeAggregate(Server $server): Cursor { addSubscriber($this); try { - return $this->aggregate->execute($server); + $cursor = $this->aggregate->execute($server); + assert($cursor instanceof Cursor); + + return $cursor; } finally { removeSubscriber($this); } @@ -368,11 +404,9 @@ private function getInitialResumeToken() * * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#resume-process * @param array|object|null $resumeToken - * @param bool $hasAdvanced - * @return ChangeStreamIterator * @throws InvalidArgumentException */ - private function resume($resumeToken = null, $hasAdvanced = false) + private function resume($resumeToken = null, bool $hasAdvanced = false): ChangeStreamIterator { if (isset($resumeToken) && ! is_array($resumeToken) && ! is_object($resumeToken)) { throw InvalidArgumentException::invalidType('$resumeToken', $resumeToken, 'array or object'); @@ -410,18 +444,18 @@ private function resume($resumeToken = null, $hasAdvanced = false) * Determine whether to capture operation time from an aggregate response. * * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#startatoperationtime - * @param Server $server - * @return boolean */ - private function shouldCaptureOperationTime(Server $server) + private function shouldCaptureOperationTime(Server $server): bool { if ($this->hasResumed) { return false; } - if (isset($this->changeStreamOptions['resumeAfter']) || + if ( + isset($this->changeStreamOptions['resumeAfter']) || isset($this->changeStreamOptions['startAfter']) || - isset($this->changeStreamOptions['startAtOperationTime'])) { + isset($this->changeStreamOptions['startAtOperationTime']) + ) { return false; } @@ -433,7 +467,7 @@ private function shouldCaptureOperationTime(Server $server) return false; } - if (! server_supports_feature($server, self::$wireVersionForStartAtOperationTime)) { + if (! server_supports_feature($server, self::WIRE_VERSION_FOR_START_AT_OPERATION_TIME)) { return false; } diff --git a/src/Operation/WithTransaction.php b/src/Operation/WithTransaction.php index 3d0474e7e..f6c1057fb 100644 --- a/src/Operation/WithTransaction.php +++ b/src/Operation/WithTransaction.php @@ -6,12 +6,11 @@ use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Session; use Throwable; + use function call_user_func; use function time; -/** - * @internal - */ +/** @internal */ class WithTransaction { /** @var callable */ @@ -51,11 +50,10 @@ public function __construct(callable $callback, array $transactionOptions = []) * @see Client::startSession * * @param Session $session A session object as retrieved by Client::startSession - * @return void * @throws RuntimeException for driver errors while committing the transaction * @throws Exception for any other errors, including those thrown in the callback */ - public function execute(Session $session) + public function execute(Session $session): void { $startTime = time(); @@ -69,7 +67,8 @@ public function execute(Session $session) $session->abortTransaction(); } - if ($e instanceof RuntimeException && + if ( + $e instanceof RuntimeException && $e->hasErrorLabel('TransientTransactionError') && ! $this->isTransactionTimeLimitExceeded($startTime) ) { @@ -88,7 +87,8 @@ public function execute(Session $session) try { $session->commitTransaction(); } catch (RuntimeException $e) { - if ($e->getCode() !== 50 /* MaxTimeMSExpired */ && + if ( + $e->getCode() !== 50 /* MaxTimeMSExpired */ && $e->hasErrorLabel('UnknownTransactionCommitResult') && ! $this->isTransactionTimeLimitExceeded($startTime) ) { @@ -96,7 +96,8 @@ public function execute(Session $session) continue; } - if ($e->hasErrorLabel('TransientTransactionError') && + if ( + $e->hasErrorLabel('TransientTransactionError') && ! $this->isTransactionTimeLimitExceeded($startTime) ) { // Restart the transaction, invoking the callback again @@ -119,9 +120,8 @@ public function execute(Session $session) * Returns whether the time limit for retrying transactions in the convenient transaction API has passed * * @param int $startTime The time the transaction was started - * @return bool */ - private function isTransactionTimeLimitExceeded($startTime) + private function isTransactionTimeLimitExceeded(int $startTime): bool { return time() - $startTime >= 120; } diff --git a/src/UpdateResult.php b/src/UpdateResult.php index 62660d2ca..8c2284ad5 100644 --- a/src/UpdateResult.php +++ b/src/UpdateResult.php @@ -1,12 +1,12 @@ getType() === Server::TYPE_LOAD_BALANCER) { + continue; + } + + if (! server_supports_feature($server, $wireVersionForWriteStageOnSecondary)) { + return false; + } + } + + return true; +} + /** * Applies a type map to a document. * @@ -63,21 +95,40 @@ function apply_type_map_to_document($document, array $typeMap) } /** - * Generate an index name from a key specification. + * Converts a document parameter to an array. + * + * This is used to facilitate unified access to document fields. It also handles + * Document, PackedArray, and Serializable objects. + * + * This function is not used for type checking. Therefore, it does not reject + * PackedArray objects or Serializable::bsonSerialize() return values that would + * encode as BSON arrays. * * @internal - * @param array|object $document Document containing fields mapped to values, - * which denote order or an index type - * @return string - * @throws InvalidArgumentException + * @param array|object $document + * @throws InvalidArgumentException if $document is not an array or object */ -function generate_index_name($document) +function document_to_array($document): array { - if ($document instanceof Serializable) { + if ($document instanceof Document || $document instanceof PackedArray) { + /* Nested documents and arrays are intentionally left as BSON. We avoid + * iterator_to_array() since Document and PackedArray iteration returns + * all values as MongoDB\BSON\Value instances. */ + + /** @psalm-var array */ + return $document->toPHP([ + 'array' => 'bson', + 'document' => 'bson', + 'root' => 'array', + ]); + } elseif ($document instanceof Serializable) { $document = $document->bsonSerialize(); } if (is_object($document)) { + /* Note: this omits all uninitialized properties, whereas BSON encoding + * includes untyped, uninitialized properties. This is acceptable given + * document_to_array()'s use cases. */ $document = get_object_vars($document); } @@ -85,79 +136,141 @@ function generate_index_name($document) throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); } - $name = ''; + return $document; +} + +/** + * Return a collection's encryptedFields from the encryptedFieldsMap + * autoEncryption driver option (if available). + * + * @internal + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#collection-encryptedfields-lookup-getencryptedfields + * @see Collection::drop() + * @see Database::createCollection() + * @see Database::dropCollection() + * @return array|object|null + */ +function get_encrypted_fields_from_driver(string $databaseName, string $collectionName, Manager $manager) +{ + $encryptedFieldsMap = (array) $manager->getEncryptedFieldsMap(); + + return $encryptedFieldsMap[$databaseName . '.' . $collectionName] ?? null; +} + +/** + * Return a collection's encryptedFields option from the server (if any). + * + * @internal + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#collection-encryptedfields-lookup-getencryptedfields + * @see Collection::drop() + * @see Database::dropCollection() + * @return array|object|null + */ +function get_encrypted_fields_from_server(string $databaseName, string $collectionName, Manager $manager, Server $server) +{ + // No-op if the encryptedFieldsMap autoEncryption driver option was omitted + if ($manager->getEncryptedFieldsMap() === null) { + return null; + } + + $collectionInfoIterator = (new ListCollections($databaseName, ['filter' => ['name' => $collectionName]]))->execute($server); - foreach ($document as $field => $type) { - $name .= ($name != '' ? '_' : '') . $field . '_' . $type; + foreach ($collectionInfoIterator as $collectionInfo) { + /* Note: ListCollections applies a typeMap that converts BSON documents + * to PHP arrays. This should not be problematic as encryptedFields here + * is only used by drop helpers to obtain names of supporting encryption + * collections. */ + return $collectionInfo['options']['encryptedFields'] ?? null; } - return $name; + return null; } /** * Return whether the first key in the document starts with a "$" character. * - * This is used for differentiating update and replacement documents. + * This is used for validating aggregation pipeline stages and differentiating + * update and replacement documents. Since true and false return values may be + * expected in different contexts, this function intentionally throws if + * $document has an unexpected type instead of returning false. * * @internal - * @param array|object $document Update or replacement document - * @return boolean - * @throws InvalidArgumentException + * @param array|object $document + * @throws InvalidArgumentException if $document is not an array or object */ -function is_first_key_operator($document) +function is_first_key_operator($document): bool { - if ($document instanceof Serializable) { - $document = $document->bsonSerialize(); - } + $document = document_to_array($document); - if (is_object($document)) { - $document = get_object_vars($document); - } + $firstKey = array_key_first($document); - if (! is_array($document)) { - throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); + if (! is_string($firstKey)) { + return false; } - reset($document); - $firstKey = (string) key($document); - - return isset($firstKey[0]) && $firstKey[0] === '$'; + return '$' === ($firstKey[0] ?? null); } /** - * Returns whether an update specification is a valid aggregation pipeline. + * Returns whether the argument is a valid aggregation or update pipeline. + * + * This is primarily used for validating arguments for update and replace + * operations, but can also be used for validating an aggregation pipeline. + * + * The $allowEmpty parameter can be used to control whether an empty array + * should be considered a valid pipeline. Empty arrays are generally valid for + * an aggregation pipeline, but the things are more complicated for update + * pipelines. + * + * Update operations must prohibit empty pipelines, since libmongoc may encode + * an empty pipeline array as an empty replacement document when writing an + * update command (arrays and documents have the same bson_t representation). + * For consistency, findOneAndUpdate should also prohibit empty pipelines. + * Replace operations (e.g. replaceOne, findOneAndReplace) should reject empty + * and non-empty pipelines alike, since neither is a replacement document. + * + * Note: this method may propagate an InvalidArgumentException from + * document_or_array() if a Serializable object within the pipeline array + * returns a non-array, non-object value from its bsonSerialize() method. * * @internal - * @param mixed $pipeline - * @return boolean + * @param array|object $pipeline + * @throws InvalidArgumentException */ -function is_pipeline($pipeline) +function is_pipeline($pipeline, bool $allowEmpty = false): bool { + if ($pipeline instanceof PackedArray) { + /* Nested documents and arrays are intentionally left as BSON. We avoid + * iterator_to_array() since PackedArray iteration returns all values as + * MongoDB\BSON\Value instances. */ + /** @psalm-var array */ + $pipeline = $pipeline->toPHP([ + 'array' => 'bson', + 'document' => 'bson', + 'root' => 'array', + ]); + } elseif ($pipeline instanceof Serializable) { + $pipeline = $pipeline->bsonSerialize(); + } + if (! is_array($pipeline)) { return false; } if ($pipeline === []) { - return false; + return $allowEmpty; } - $expectedKey = 0; + if (! array_is_list($pipeline)) { + return false; + } - foreach ($pipeline as $key => $stage) { + foreach ($pipeline as $stage) { if (! is_array($stage) && ! is_object($stage)) { return false; } - if ($expectedKey !== $key) { - return false; - } - - $expectedKey++; - $stage = (array) $stage; - reset($stage); - $key = key($stage); - - if (! isset($key[0]) || $key[0] !== '$') { + if (! is_first_key_operator($stage)) { return false; } } @@ -170,9 +283,8 @@ function is_pipeline($pipeline) * * @internal * @param array $options Command options - * @return boolean */ -function is_in_transaction(array $options) +function is_in_transaction(array $options): bool { if (isset($options['session']) && $options['session'] instanceof Session && $options['session']->isInTransaction()) { return true; @@ -188,10 +300,9 @@ function is_in_transaction(array $options) * executed against a primary server. * * @internal - * @param array $pipeline List of pipeline operations - * @return boolean + * @param array $pipeline Aggregation pipeline */ -function is_last_pipeline_operator_write(array $pipeline) +function is_last_pipeline_operator_write(array $pipeline): bool { $lastOp = end($pipeline); @@ -199,9 +310,13 @@ function is_last_pipeline_operator_write(array $pipeline) return false; } - $lastOp = (array) $lastOp; + if (! is_array($lastOp) && ! is_object($lastOp)) { + return false; + } + + $key = array_key_first(document_to_array($lastOp)); - return in_array(key($lastOp), ['$out', '$merge'], true); + return $key === '$merge' || $key === '$out'; } /** @@ -210,32 +325,33 @@ function is_last_pipeline_operator_write(array $pipeline) * This is used to determine if a mapReduce command requires a primary. * * @internal - * @see https://docs.mongodb.com/manual/reference/command/mapReduce/#output-inline + * @see https://mongodb.com/docs/manual/reference/command/mapReduce/#output-inline * @param string|array|object $out Output specification - * @return boolean - * @throws InvalidArgumentException */ -function is_mapreduce_output_inline($out) +function is_mapreduce_output_inline($out): bool { if (! is_array($out) && ! is_object($out)) { return false; } - if ($out instanceof Serializable) { - $out = $out->bsonSerialize(); - } - - if (is_object($out)) { - $out = get_object_vars($out); - } - - if (! is_array($out)) { - throw InvalidArgumentException::invalidType('$out', $out, 'array or object'); - } - - reset($out); + return array_key_first(document_to_array($out)) === 'inline'; +} - return key($out) === 'inline'; +/** + * Return whether the write concern is acknowledged. + * + * This function is similar to mongoc_write_concern_is_acknowledged but does not + * check the fsync option since that was never supported in the PHP driver. + * + * @internal + * @see https://mongodb.com/docs/manual/reference/write-concern/ + */ +function is_write_concern_acknowledged(WriteConcern $writeConcern): bool +{ + /* Note: -1 corresponds to MONGOC_WRITE_CONCERN_W_ERRORS_IGNORED, which is + * deprecated synonym of MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED and slated + * for removal in libmongoc 2.0. */ + return ($writeConcern->getW() !== 0 && $writeConcern->getW() !== -1) || $writeConcern->getJournal() === true; } /** @@ -244,9 +360,8 @@ function is_mapreduce_output_inline($out) * @internal * @param Server $server Server to check * @param integer $feature Feature constant (i.e. wire protocol version) - * @return boolean */ -function server_supports_feature(Server $server, $feature) +function server_supports_feature(Server $server, int $feature): bool { $info = $server->getInfo(); $maxWireVersion = isset($info['maxWireVersion']) ? (integer) $info['maxWireVersion'] : 0; @@ -255,11 +370,18 @@ function server_supports_feature(Server $server, $feature) return $minWireVersion <= $feature && $maxWireVersion >= $feature; } -function is_string_array($input) +/** + * Return whether the input is an array of strings. + * + * @internal + * @param mixed $input + */ +function is_string_array($input): bool { if (! is_array($input)) { return false; } + foreach ($input as $item) { if (! is_string($item)) { return false; @@ -313,9 +435,8 @@ function recursive_copy($element) * @internal * @param array $typeMap The existing typeMap * @param string $fieldPath The field path to apply the root type to - * @return array */ -function create_field_path_type_map(array $typeMap, $fieldPath) +function create_field_path_type_map(array $typeMap, string $fieldPath): array { // If some field paths already exist, we prefix them with the field path we are assuming as the new root if (isset($typeMap['fieldPaths']) && is_array($typeMap['fieldPaths'])) { @@ -360,17 +481,16 @@ function create_field_path_type_map(array $typeMap, $fieldPath) * from the initial call have elapsed. After that, no retries will happen and * the helper will throw the last exception received from the driver. * - * @see Client::startSession - * @see Session::startTransaction for supported transaction options + * @see Client::startSession() + * @see Session::startTransaction() for supported transaction options * * @param Session $session A session object as retrieved by Client::startSession * @param callable $callback A callback that will be invoked within the transaction * @param array $transactionOptions Additional options that are passed to Session::startTransaction - * @return void * @throws RuntimeException for driver errors while committing the transaction * @throws Exception for any other errors, including those thrown in the callback */ -function with_transaction(Session $session, callable $callback, array $transactionOptions = []) +function with_transaction(Session $session, callable $callback, array $transactionOptions = []): void { $operation = new WithTransaction($callback, $transactionOptions); $operation->execute($session); @@ -380,10 +500,8 @@ function with_transaction(Session $session, callable $callback, array $transacti * Returns the session option if it is set and valid. * * @internal - * @param array $options - * @return Session|null */ -function extract_session_from_options(array $options) +function extract_session_from_options(array $options): ?Session { if (! isset($options['session']) || ! $options['session'] instanceof Session) { return null; @@ -396,10 +514,8 @@ function extract_session_from_options(array $options) * Returns the readPreference option if it is set and valid. * * @internal - * @param array $options - * @return ReadPreference|null */ -function extract_read_preference_from_options(array $options) +function extract_read_preference_from_options(array $options): ?ReadPreference { if (! isset($options['readPreference']) || ! $options['readPreference'] instanceof ReadPreference) { return null; @@ -413,20 +529,68 @@ function extract_read_preference_from_options(array $options) * (if given) * * @internal - * @return Server */ -function select_server(Manager $manager, array $options) +function select_server(Manager $manager, array $options): Server { $session = extract_session_from_options($options); - if ($session instanceof Session && $session->getServer() !== null) { - return $session->getServer(); + $server = $session instanceof Session ? $session->getServer() : null; + if ($server !== null) { + return $server; } $readPreference = extract_read_preference_from_options($options); if (! $readPreference instanceof ReadPreference) { // TODO: PHPLIB-476: Read transaction read preference once PHPC-1439 is implemented - $readPreference = new ReadPreference(ReadPreference::RP_PRIMARY); + $readPreference = new ReadPreference(ReadPreference::PRIMARY); } return $manager->selectServer($readPreference); } + +/** + * Performs server selection for an aggregate operation with a write stage. The + * $options parameter may be modified by reference if a primary read preference + * must be forced due to the existence of pre-5.0 servers in the topology. + * + * @internal + * @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#aggregation-pipelines-with-write-stages + */ +function select_server_for_aggregate_write_stage(Manager $manager, array &$options): Server +{ + $readPreference = extract_read_preference_from_options($options); + + /* If there is either no read preference or a primary read preference, there + * is no special server selection logic to apply. */ + if ($readPreference === null || $readPreference->getModeString() === ReadPreference::PRIMARY) { + return select_server($manager, $options); + } + + $server = null; + $serverSelectionError = null; + + try { + $server = select_server($manager, $options); + } catch (DriverRuntimeException $serverSelectionError) { + } + + /* If any pre-5.0 servers exist in the topology, force a primary read + * preference and repeat server selection if it previously failed or + * selected a secondary. */ + if (! all_servers_support_write_stage_on_secondary($manager->getServers())) { + $options['readPreference'] = new ReadPreference(ReadPreference::PRIMARY); + + if ($server === null || $server->isSecondary()) { + return select_server($manager, $options); + } + } + + /* If the topology only contains 5.0+ servers, we should either return the + * previously selected server or propagate the server selection error. */ + if ($serverSelectionError !== null) { + throw $serverSelectionError; + } + + assert($server instanceof Server); + + return $server; +} diff --git a/tests/ClientFunctionalTest.php b/tests/ClientFunctionalTest.php index 59d71cea9..6535c66da 100644 --- a/tests/ClientFunctionalTest.php +++ b/tests/ClientFunctionalTest.php @@ -8,36 +8,33 @@ use MongoDB\Driver\Session; use MongoDB\Model\DatabaseInfo; use MongoDB\Model\DatabaseInfoIterator; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; + use function call_user_func; use function is_callable; use function sprintf; -use function version_compare; /** * Functional tests for the Client class. */ class ClientFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; - /** @var Client */ private $client; - private function doSetUp() + public function setUp(): void { parent::setUp(); - $this->client = new Client(static::getUri()); + $this->client = static::createTestClient(); $this->client->dropDatabase($this->getDatabaseName()); } - public function testGetManager() + public function testGetManager(): void { $this->assertInstanceOf(Manager::class, $this->client->getManager()); } - public function testDropDatabase() + public function testDropDatabase(): void { $bulkWrite = new BulkWrite(); $bulkWrite->insert(['x' => 1]); @@ -50,7 +47,7 @@ public function testDropDatabase() $this->assertCollectionCount($this->getNamespace(), 0); } - public function testListDatabases() + public function testListDatabases(): void { $bulkWrite = new BulkWrite(); $bulkWrite->insert(['x' => 1]); @@ -66,13 +63,13 @@ public function testListDatabases() $this->assertInstanceOf(DatabaseInfo::class, $database); } - $this->assertDatabaseExists($this->getDatabaseName(), function (DatabaseInfo $info) { + $this->assertDatabaseExists($this->getDatabaseName(), function (DatabaseInfo $info): void { $this->assertFalse($info->isEmpty()); $this->assertGreaterThan(0, $info->getSizeOnDisk()); }); } - public function testListDatabaseNames() + public function testListDatabaseNames(): void { $bulkWrite = new BulkWrite(); $bulkWrite->insert(['x' => 1]); @@ -94,11 +91,8 @@ public function testListDatabaseNames() * argument as its first and only parameter. If a DatabaseInfo matching * the given name is found, it will be passed to the callback, which may * perform additional assertions. - * - * @param string $databaseName - * @param callable $callback */ - private function assertDatabaseExists($databaseName, $callback = null) + private function assertDatabaseExists(string $databaseName, ?callable $callback = null): void { if ($callback !== null && ! is_callable($callback)) { throw new InvalidArgumentException('$callback is not a callable'); @@ -122,11 +116,8 @@ private function assertDatabaseExists($databaseName, $callback = null) } } - public function testStartSession() + public function testStartSession(): void { - if (version_compare($this->getFeatureCompatibilityVersion(), '3.6', '<')) { - $this->markTestSkipped('startSession() is only supported on FCV 3.6 or higher'); - } $this->assertInstanceOf(Session::class, $this->client->startSession()); } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 55d3a13a5..04855d479 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -15,17 +15,15 @@ */ class ClientTest extends TestCase { - public function testConstructorDefaultUri() + public function testConstructorDefaultUri(): void { $client = new Client(); $this->assertEquals('mongodb://127.0.0.1/', (string) $client); } - /** - * @doesNotPerformAssertions - */ - public function testConstructorAutoEncryptionOpts() + /** @doesNotPerformAssertions */ + public function testConstructorAutoEncryptionOpts(): void { $autoEncryptionOpts = [ 'keyVaultClient' => new Client(static::getUri()), @@ -36,10 +34,8 @@ public function testConstructorAutoEncryptionOpts() new Client(static::getUri(), [], ['autoEncryption' => $autoEncryptionOpts]); } - /** - * @dataProvider provideInvalidConstructorDriverOptions - */ - public function testConstructorDriverOptionTypeChecks(array $driverOptions, string $exception = InvalidArgumentException::class) + /** @dataProvider provideInvalidConstructorDriverOptions */ + public function testConstructorDriverOptionTypeChecks(array $driverOptions, string $exception = InvalidArgumentException::class): void { $this->expectException($exception); new Client(static::getUri(), [], $driverOptions); @@ -73,14 +69,14 @@ public function provideInvalidConstructorDriverOptions() return $options; } - public function testToString() + public function testToString(): void { $client = new Client(static::getUri()); $this->assertSame(static::getUri(), (string) $client); } - public function testSelectCollectionInheritsOptions() + public function testSelectCollectionInheritsOptions(): void { $uriOptions = [ 'readConcernLevel' => ReadConcern::LOCAL, @@ -99,18 +95,18 @@ public function testSelectCollectionInheritsOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testSelectCollectionPassesOptions() + public function testSelectCollectionPassesOptions(): void { $collectionOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -122,14 +118,14 @@ public function testSelectCollectionPassesOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testGetSelectsDatabaseAndInheritsOptions() + public function testGetSelectsDatabaseAndInheritsOptions(): void { $uriOptions = ['w' => WriteConcern::MAJORITY]; @@ -142,11 +138,11 @@ public function testGetSelectsDatabaseAndInheritsOptions() $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testSelectDatabaseInheritsOptions() + public function testSelectDatabaseInheritsOptions(): void { $uriOptions = [ 'readConcernLevel' => ReadConcern::LOCAL, - 'readPreference' => 'secondaryPreferred', + 'readPreference' => ReadPreference::SECONDARY_PREFERRED, 'w' => WriteConcern::MAJORITY, ]; @@ -161,18 +157,18 @@ public function testSelectDatabaseInheritsOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testSelectDatabasePassesOptions() + public function testSelectDatabasePassesOptions(): void { $databaseOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -184,14 +180,14 @@ public function testSelectDatabasePassesOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testCreateClientEncryption() + public function testCreateClientEncryption(): void { $client = new Client(static::getUri()); @@ -204,7 +200,7 @@ public function testCreateClientEncryption() $this->assertInstanceOf(ClientEncryption::class, $clientEncryption); } - public function testCreateClientEncryptionWithKeyVaultClient() + public function testCreateClientEncryptionWithKeyVaultClient(): void { $client = new Client(static::getUri()); @@ -218,7 +214,7 @@ public function testCreateClientEncryptionWithKeyVaultClient() $this->assertInstanceOf(ClientEncryption::class, $clientEncryption); } - public function testCreateClientEncryptionWithManager() + public function testCreateClientEncryptionWithManager(): void { $client = new Client(static::getUri()); @@ -232,7 +228,7 @@ public function testCreateClientEncryptionWithManager() $this->assertInstanceOf(ClientEncryption::class, $clientEncryption); } - public function testCreateClientEncryptionWithInvalidKeyVaultClient() + public function testCreateClientEncryptionWithInvalidKeyVaultClient(): void { $client = new Client(static::getUri()); diff --git a/tests/Collection/CollectionFunctionalTest.php b/tests/Collection/CollectionFunctionalTest.php index 66b20a65a..24c5903b9 100644 --- a/tests/Collection/CollectionFunctionalTest.php +++ b/tests/Collection/CollectionFunctionalTest.php @@ -5,6 +5,7 @@ use Closure; use MongoDB\BSON\Javascript; use MongoDB\Collection; +use MongoDB\Database; use MongoDB\Driver\BulkWrite; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadPreference; @@ -14,6 +15,8 @@ use MongoDB\MapReduceResult; use MongoDB\Operation\Count; use MongoDB\Tests\CommandObserver; +use TypeError; + use function array_filter; use function call_user_func; use function is_scalar; @@ -27,22 +30,18 @@ */ class CollectionFunctionalTest extends FunctionalTestCase { - /** - * @dataProvider provideInvalidDatabaseAndCollectionNames - */ - public function testConstructorDatabaseNameArgument($databaseName) + /** @dataProvider provideInvalidDatabaseAndCollectionNames */ + public function testConstructorDatabaseNameArgument($databaseName, string $expectedExceptionClass): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($expectedExceptionClass); // TODO: Move to unit test once ManagerInterface can be mocked (PHPC-378) new Collection($this->manager, $databaseName, $this->getCollectionName()); } - /** - * @dataProvider provideInvalidDatabaseAndCollectionNames - */ - public function testConstructorCollectionNameArgument($collectionName) + /** @dataProvider provideInvalidDatabaseAndCollectionNames */ + public function testConstructorCollectionNameArgument($collectionName, string $expectedExceptionClass): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($expectedExceptionClass); // TODO: Move to unit test once ManagerInterface can be mocked (PHPC-378) new Collection($this->manager, $this->getDatabaseName(), $collectionName); } @@ -50,15 +49,13 @@ public function testConstructorCollectionNameArgument($collectionName) public function provideInvalidDatabaseAndCollectionNames() { return [ - [null], - [''], + [null, TypeError::class], + ['', InvalidArgumentException::class], ]; } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $options); @@ -87,37 +84,37 @@ public function provideInvalidConstructorOptions() return $options; } - public function testGetManager() + public function testGetManager(): void { $this->assertSame($this->manager, $this->collection->getManager()); } - public function testToString() + public function testToString(): void { $this->assertEquals($this->getNamespace(), (string) $this->collection); } - public function getGetCollectionName() + public function getGetCollectionName(): void { $this->assertEquals($this->getCollectionName(), $this->collection->getCollectionName()); } - public function getGetDatabaseName() + public function getGetDatabaseName(): void { $this->assertEquals($this->getDatabaseName(), $this->collection->getDatabaseName()); } - public function testGetNamespace() + public function testGetNamespace(): void { $this->assertEquals($this->getNamespace(), $this->collection->getNamespace()); } - public function testAggregateWithinTransaction() + public function testAggregateWithinTransaction(): void { $this->skipIfTransactionsAreNotSupported(); // Collection must be created before the transaction starts - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -143,14 +140,10 @@ public function testAggregateWithinTransaction() } } - public function testCreateIndexSplitsCommandOptions() + public function testCreateIndexSplitsCommandOptions(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $this->collection->createIndex( ['x' => 1], [ @@ -162,7 +155,7 @@ function () { ] ); }, - function (array $event) { + function (array $event): void { $command = $event['started']->getCommand(); $this->assertObjectHasAttribute('lsid', $command); $this->assertObjectHasAttribute('maxTimeMS', $command); @@ -173,10 +166,8 @@ function (array $event) { ); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ - public function testDistinctWithTypeMap(array $typeMap, array $expectedDocuments) + /** @dataProvider provideTypeMapOptionsAndExpectedDocuments */ + public function testDistinctWithTypeMap(array $typeMap, array $expectedDocuments): void { $bulkWrite = new BulkWrite(['ordered' => true]); $bulkWrite->insert([ @@ -255,26 +246,24 @@ public function provideTypeMapOptionsAndExpectedDocuments() ]; } - public function testDrop() + public function testDrop(): void { $writeResult = $this->collection->insertOne(['x' => 1]); $this->assertEquals(1, $writeResult->getInsertedCount()); $commandResult = $this->collection->drop(); $this->assertCommandSucceeded($commandResult); - $this->assertCollectionCount($this->getNamespace(), 0); + $this->assertCollectionDoesNotExist($this->getCollectionName()); } - /** - * @todo Move this to a unit test once Manager can be mocked - */ - public function testDropIndexShouldNotAllowWildcardCharacter() + /** @todo Move this to a unit test once Manager can be mocked */ + public function testDropIndexShouldNotAllowWildcardCharacter(): void { $this->expectException(InvalidArgumentException::class); $this->collection->dropIndex('*'); } - public function testExplain() + public function testExplain(): void { $this->createFixtures(3); @@ -285,7 +274,7 @@ public function testExplain() $this->assertArrayHasKey('queryPlanner', $result); } - public function testFindOne() + public function testFindOne(): void { $this->createFixtures(5); @@ -300,12 +289,12 @@ public function testFindOne() $this->assertSameDocument($expected, $this->collection->findOne($filter, $options)); } - public function testFindWithinTransaction() + public function testFindWithinTransaction(): void { $this->skipIfTransactionsAreNotSupported(); // Collection must be created before the transaction starts - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -331,11 +320,58 @@ public function testFindWithinTransaction() } } - public function testWithOptionsInheritsOptions() + public function testRenameToSameDatabase(): void + { + $toCollectionName = $this->getCollectionName() . '.renamed'; + $toCollection = new Collection($this->manager, $this->getDatabaseName(), $toCollectionName); + + $writeResult = $this->collection->insertOne(['_id' => 1]); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $commandResult = $this->collection->rename($toCollectionName, null, ['dropTarget' => true]); + $this->assertCommandSucceeded($commandResult); + $this->assertCollectionDoesNotExist($this->getCollectionName()); + $this->assertCollectionExists($toCollectionName); + + $this->assertSameDocument(['_id' => 1], $toCollection->findOne()); + $toCollection->drop(); + } + + public function testRenameToDifferentDatabase(): void + { + $toDatabaseName = $this->getDatabaseName() . '_renamed'; + $toDatabase = new Database($this->manager, $toDatabaseName); + + /* When renaming an unsharded collection, mongos requires the source + * and target database to both exist on the primary shard. In practice, + * this means we need to create the target database explicitly. + * See: https://mongodb.com/docs/manual/reference/command/renameCollection/#unsharded-collections + */ + if ($this->isShardedCluster()) { + $toDatabase->foo->insertOne(['_id' => 1]); + } + + $toCollectionName = $this->getCollectionName() . '.renamed'; + $toCollection = new Collection($this->manager, $toDatabaseName, $toCollectionName); + + $writeResult = $this->collection->insertOne(['_id' => 1]); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $commandResult = $this->collection->rename($toCollectionName, $toDatabaseName); + $this->assertCommandSucceeded($commandResult); + $this->assertCollectionDoesNotExist($this->getCollectionName()); + $this->assertCollectionExists($toCollectionName, $toDatabaseName); + + $this->assertSameDocument(['_id' => 1], $toCollection->findOne()); + + $toDatabase->drop(); + } + + public function testWithOptionsInheritsOptions(): void { $collectionOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -350,18 +386,18 @@ public function testWithOptionsInheritsOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testWithOptionsPassesOptions() + public function testWithOptionsPassesOptions(): void { $collectionOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -372,14 +408,20 @@ public function testWithOptionsPassesOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testMapReduce() + /** + * @group matrix-testing-exclude-server-4.4-driver-4.0 + * @group matrix-testing-exclude-server-4.4-driver-4.2 + * @group matrix-testing-exclude-server-5.0-driver-4.0 + * @group matrix-testing-exclude-server-5.0-driver-4.2 + */ + public function testMapReduce(): void { $this->createFixtures(3); @@ -406,7 +448,7 @@ public function collectionMethodClosures() { return [ [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->aggregate( [['$match' => ['_id' => ['$lt' => 3]]]], ['session' => $session] + $options @@ -415,7 +457,7 @@ function ($collection, $session, $options = []) { ], [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->bulkWrite( [['insertOne' => [['test' => 'foo']]]], ['session' => $session] + $options @@ -435,7 +477,7 @@ function($collection, $session, $options = []) { */ [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->countDocuments( [], ['session' => $session] + $options @@ -455,7 +497,7 @@ function($collection, $session, $options = []) { */ [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->deleteMany( ['test' => 'foo'], ['session' => $session] + $options @@ -464,7 +506,7 @@ function ($collection, $session, $options = []) { ], [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->deleteOne( ['test' => 'foo'], ['session' => $session] + $options @@ -473,7 +515,7 @@ function ($collection, $session, $options = []) { ], [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->distinct( '_id', [], @@ -523,7 +565,7 @@ function($collection, $session, $options = []) { */ [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->find( ['test' => 'foo'], ['session' => $session] + $options @@ -532,7 +574,7 @@ function ($collection, $session, $options = []) { ], [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->findOne( ['test' => 'foo'], ['session' => $session] + $options @@ -541,7 +583,7 @@ function ($collection, $session, $options = []) { ], [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->findOneAndDelete( ['test' => 'foo'], ['session' => $session] + $options @@ -550,7 +592,7 @@ function ($collection, $session, $options = []) { ], [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->findOneAndReplace( ['test' => 'foo'], [], @@ -560,7 +602,7 @@ function ($collection, $session, $options = []) { ], [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->findOneAndUpdate( ['test' => 'foo'], ['$set' => ['updated' => 1]], @@ -570,7 +612,7 @@ function ($collection, $session, $options = []) { ], [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->insertMany( [ ['test' => 'foo'], @@ -582,7 +624,7 @@ function ($collection, $session, $options = []) { ], [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->insertOne( ['test' => 'foo'], ['session' => $session] + $options @@ -614,7 +656,7 @@ function($collection, $session, $options = []) { */ [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->replaceOne( ['test' => 'foo'], [], @@ -624,7 +666,7 @@ function ($collection, $session, $options = []) { ], [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->updateMany( ['test' => 'foo'], ['$set' => ['updated' => 1]], @@ -634,7 +676,7 @@ function ($collection, $session, $options = []) { ], [ - function ($collection, $session, $options = []) { + function ($collection, $session, $options = []): void { $collection->updateOne( ['test' => 'foo'], ['$set' => ['updated' => 1]], @@ -680,14 +722,12 @@ function ($rw) { ); } - /** - * @dataProvider collectionMethodClosures - */ - public function testMethodDoesNotInheritReadWriteConcernInTranasaction(Closure $method) + /** @dataProvider collectionMethodClosures */ + public function testMethodDoesNotInheritReadWriteConcernInTranasaction(Closure $method): void { $this->skipIfTransactionsAreNotSupported(); - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -698,24 +738,22 @@ public function testMethodDoesNotInheritReadWriteConcernInTranasaction(Closure $ ]); (new CommandObserver())->observe( - function () use ($method, $collection, $session) { + function () use ($method, $collection, $session): void { call_user_func($method, $collection, $session); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); } ); } - /** - * @dataProvider collectionWriteMethodClosures - */ - public function testMethodInTransactionWithWriteConcernOption($method) + /** @dataProvider collectionWriteMethodClosures */ + public function testMethodInTransactionWithWriteConcernOption($method): void { $this->skipIfTransactionsAreNotSupported(); - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -730,14 +768,12 @@ public function testMethodInTransactionWithWriteConcernOption($method) } } - /** - * @dataProvider collectionReadMethodClosures - */ - public function testMethodInTransactionWithReadConcernOption($method) + /** @dataProvider collectionReadMethodClosures */ + public function testMethodInTransactionWithReadConcernOption($method): void { $this->skipIfTransactionsAreNotSupported(); - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -754,11 +790,8 @@ public function testMethodInTransactionWithReadConcernOption($method) /** * Create data fixtures. - * - * @param integer $n - * @param array $executeBulkWriteOptions */ - private function createFixtures($n, array $executeBulkWriteOptions = []) + private function createFixtures(int $n, array $executeBulkWriteOptions = []): void { $bulkWrite = new BulkWrite(['ordered' => true]); diff --git a/tests/Collection/CrudSpecFunctionalTest.php b/tests/Collection/CrudSpecFunctionalTest.php index 0b33dcbc0..994ec915b 100644 --- a/tests/Collection/CrudSpecFunctionalTest.php +++ b/tests/Collection/CrudSpecFunctionalTest.php @@ -2,7 +2,6 @@ namespace MongoDB\Tests\Collection; -use IteratorIterator; use LogicException; use MongoDB\BulkWriteResult; use MongoDB\Collection; @@ -14,8 +13,7 @@ use MongoDB\Operation\FindOneAndReplace; use MongoDB\UpdateResult; use MultipleIterator; -use PHPUnit_Framework_SkippedTestError; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; + use function array_diff_key; use function array_key_exists; use function array_map; @@ -26,37 +24,44 @@ use function sprintf; use function str_replace; use function strtolower; -use function version_compare; /** * CRUD spec functional tests. * * @see https://github.com/mongodb/specifications/tree/master/source/crud/tests + * + * @group serverless + * @group matrix-testing-exclude-server-5.0-driver-4.0 */ class CrudSpecFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; + public const SERVERLESS_ALLOW = 'allow'; + public const SERVERLESS_FORBID = 'forbid'; + public const SERVERLESS_REQUIRE = 'require'; /** @var Collection */ private $expectedCollection; - private function doSetUp() + public function setUp(): void { parent::setUp(); - $this->expectedCollection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName() . '.expected'); - $this->expectedCollection->drop(); + $this->expectedCollection = $this->dropCollection($this->getDatabaseName(), $this->getCollectionName() . '.expected'); } - /** - * @dataProvider provideSpecificationTests - */ - public function testSpecification(array $initialData, array $test, $minServerVersion, $maxServerVersion) + /** @dataProvider provideSpecificationTests */ + public function testSpecification(array $initialData, array $test, $minServerVersion, $maxServerVersion, $serverless): void { - if (isset($minServerVersion) || isset($maxServerVersion)) { - $this->checkServerVersion($minServerVersion, $maxServerVersion); + if (isset($minServerVersion)) { + $this->skipIfServerVersion('<', $minServerVersion); + } + + if (isset($maxServerVersion)) { + $this->skipIfServerVersion('>=', $maxServerVersion); } + $this->checkServerlessRequirement($serverless); + $expectedData = $test['outcome']['collection']['data'] ?? null; $this->initializeData($initialData, $expectedData); @@ -84,12 +89,15 @@ public function provideSpecificationTests() foreach (glob(__DIR__ . '/spec-tests/*/*.json') as $filename) { $json = json_decode(file_get_contents($filename), true); - $minServerVersion = $json['minServerVersion'] ?? null; - $maxServerVersion = $json['maxServerVersion'] ?? null; - foreach ($json['tests'] as $test) { $name = str_replace(' ', '_', $test['description']); - $testArgs[$name] = [$json['data'], $test, $minServerVersion, $maxServerVersion]; + $testArgs[$name] = [ + $json['data'], + $test, + $json['minServerVersion'] ?? null, + $json['maxServerVersion'] ?? null, + $json['serverless'] ?? null, + ]; } } @@ -98,46 +106,48 @@ public function provideSpecificationTests() /** * Assert that the collections contain equivalent documents. - * - * @param Collection $expectedCollection - * @param Collection $actualCollection */ - private function assertEquivalentCollections($expectedCollection, $actualCollection) + private function assertEquivalentCollections(Collection $expectedCollection, Collection $actualCollection): void { $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); - $mi->attachIterator(new IteratorIterator($expectedCollection->find())); - $mi->attachIterator(new IteratorIterator($actualCollection->find())); + $mi->attachIterator($expectedCollection->find()); + $mi->attachIterator($actualCollection->find()); foreach ($mi as $documents) { - list($expectedDocument, $actualDocument) = $documents; + [$expectedDocument, $actualDocument] = $documents; $this->assertSameDocument($expectedDocument, $actualDocument); } } - /** - * Checks that the server version is within the allowed bounds (if any). - * - * @param string|null $minServerVersion - * @param string|null $maxServerVersion - * @throws PHPUnit_Framework_SkippedTestError - */ - private function checkServerVersion($minServerVersion, $maxServerVersion) + private function checkServerlessRequirement(?string $serverless): void { - $serverVersion = $this->getServerVersion(); + switch ($serverless) { + case null: + case self::SERVERLESS_ALLOW: + return; + + case self::SERVERLESS_FORBID: + if ($this->isServerless()) { + $this->markTestSkipped('Test does not apply on serverless'); + } - if (isset($minServerVersion) && version_compare($serverVersion, $minServerVersion, '<')) { - $this->markTestSkipped(sprintf('Server version "%s" < minServerVersion "%s"', $serverVersion, $minServerVersion)); - } + return; + + case self::SERVERLESS_REQUIRE: + if (! $this->isServerless()) { + $this->markTestSkipped('Test requires serverless'); + } - if (isset($maxServerVersion) && version_compare($serverVersion, $maxServerVersion, '>=')) { - $this->markTestSkipped(sprintf('Server version "%s" >= maxServerVersion "%s"', $serverVersion, $maxServerVersion)); + return; + + default: + $this->fail(sprintf('Unknown serverless requirement "%s".', $serverless)); } } /** * Executes an "operation" block. * - * @param array $operation * @return mixed * @throws LogicException if the operation is unsupported */ @@ -149,11 +159,13 @@ private function executeOperation(array $operation) $operation['arguments']['pipeline'], array_diff_key($operation['arguments'], ['pipeline' => 1]) ); + case 'bulkWrite': return $this->collection->bulkWrite( array_map([$this, 'prepareBulkWriteRequest'], $operation['arguments']['requests']), $operation['arguments']['options'] ?? [] ); + case 'count': case 'countDocuments': case 'find': @@ -161,8 +173,10 @@ private function executeOperation(array $operation) $operation['arguments']['filter'] ?? [], array_diff_key($operation['arguments'], ['filter' => 1]) ); + case 'estimatedDocumentCount': return $this->collection->estimatedDocumentCount($operation['arguments']); + case 'deleteMany': case 'deleteOne': case 'findOneAndDelete': @@ -170,12 +184,14 @@ private function executeOperation(array $operation) $operation['arguments']['filter'], array_diff_key($operation['arguments'], ['filter' => 1]) ); + case 'distinct': return $this->collection->distinct( $operation['arguments']['fieldName'], $operation['arguments']['filter'] ?? [], array_diff_key($operation['arguments'], ['fieldName' => 1, 'filter' => 1]) ); + case 'findOneAndReplace': $operation['arguments'] = $this->prepareFindAndModifyArguments($operation['arguments']); // Fall through @@ -186,6 +202,7 @@ private function executeOperation(array $operation) $operation['arguments']['replacement'], array_diff_key($operation['arguments'], ['filter' => 1, 'replacement' => 1]) ); + case 'findOneAndUpdate': $operation['arguments'] = $this->prepareFindAndModifyArguments($operation['arguments']); // Fall through @@ -197,16 +214,19 @@ private function executeOperation(array $operation) $operation['arguments']['update'], array_diff_key($operation['arguments'], ['filter' => 1, 'update' => 1]) ); + case 'insertMany': return $this->collection->insertMany( $operation['arguments']['documents'], $operation['arguments']['options'] ?? [] ); + case 'insertOne': return $this->collection->insertOne( $operation['arguments']['document'], array_diff_key($operation['arguments'], ['document' => 1]) ); + default: throw new LogicException('Unsupported operation: ' . $operation['name']); } @@ -215,14 +235,11 @@ private function executeOperation(array $operation) /** * Executes an "outcome" block. * - * @param array $operation - * @param array $outcome - * @param mixed $result - * @param RuntimeException $exception + * @param mixed $result * @return mixed * @throws LogicException if the operation is unsupported */ - private function executeOutcome(array $operation, array $outcome, $result, RuntimeException $exception = null) + private function executeOutcome(array $operation, array $outcome, $result, ?RuntimeException $exception = null) { $expectedError = array_key_exists('error', $outcome) ? $outcome['error'] : false; @@ -255,8 +272,6 @@ private function executeOutcome(array $operation, array $outcome, $result, Runti * * If no result can be extracted, null will be returned. * - * @param array $operation - * @param RuntimeException $exception * @return mixed */ private function extractResultFromException(array $operation, array $outcome, RuntimeException $exception) @@ -268,6 +283,7 @@ private function extractResultFromException(array $operation, array $outcome, Ru if ($exception instanceof BulkWriteException) { return new BulkWriteResult($exception->getWriteResult(), $insertedIds); } + break; case 'insertMany': @@ -276,6 +292,7 @@ private function extractResultFromException(array $operation, array $outcome, Ru if ($exception instanceof BulkWriteException) { return new InsertManyResult($exception->getWriteResult(), $insertedIds); } + break; } @@ -285,12 +302,11 @@ private function extractResultFromException(array $operation, array $outcome, Ru /** * Executes the "result" section of an "outcome" block. * - * @param array $operation * @param mixed $expectedResult * @param mixed $actualResult * @throws LogicException if the operation is unsupported */ - private function executeAssertResult(array $operation, $expectedResult, $actualResult) + private function executeAssertResult(array $operation, $expectedResult, $actualResult): void { switch ($operation['name']) { case 'aggregate': @@ -303,6 +319,7 @@ private function executeAssertResult(array $operation, $expectedResult, $actualR if (! is_last_pipeline_operator_write($operation['arguments']['pipeline'])) { $this->assertSameDocuments($expectedResult, $actualResult); } + break; case 'bulkWrite': @@ -342,6 +359,7 @@ private function executeAssertResult(array $operation, $expectedResult, $actualR ['upsertedIds' => $actualResult->getUpsertedIds()] ); } + break; case 'count': @@ -369,6 +387,7 @@ private function executeAssertResult(array $operation, $expectedResult, $actualR if (isset($expectedResult['deletedCount'])) { $this->assertSame($expectedResult['deletedCount'], $actualResult->getDeletedCount()); } + break; case 'findOneAndDelete': @@ -394,6 +413,7 @@ private function executeAssertResult(array $operation, $expectedResult, $actualR ['insertedIds' => $actualResult->getInsertedIds()] ); } + break; case 'insertOne': @@ -410,6 +430,7 @@ private function executeAssertResult(array $operation, $expectedResult, $actualR ['insertedId' => $actualResult->getInsertedId()] ); } + break; case 'replaceOne': @@ -436,6 +457,7 @@ private function executeAssertResult(array $operation, $expectedResult, $actualR ['upsertedId' => $actualResult->getUpsertedId()] ); } + break; default: @@ -445,11 +467,8 @@ private function executeAssertResult(array $operation, $expectedResult, $actualR /** * Initializes data in the test collections. - * - * @param array $initialData - * @param array $expectedData */ - private function initializeData(array $initialData, array $expectedData = null) + private function initializeData(array $initialData, ?array $expectedData = null): void { if (! empty($initialData)) { $this->collection->insertMany($initialData); @@ -462,11 +481,8 @@ private function initializeData(array $initialData, array $expectedData = null) /** * Prepares a request element for a bulkWrite operation. - * - * @param array $request - * @return array */ - private function prepareBulkWriteRequest(array $request) + private function prepareBulkWriteRequest(array $request): array { switch ($request['name']) { case 'deleteMany': @@ -477,8 +493,10 @@ private function prepareBulkWriteRequest(array $request) array_diff_key($request['arguments'], ['filter' => 1]), ], ]; + case 'insertOne': - return [ 'insertOne' => [ $request['arguments']['document'] ]]; + return ['insertOne' => [$request['arguments']['document']]]; + case 'replaceOne': return [ 'replaceOne' => [ @@ -487,6 +505,7 @@ private function prepareBulkWriteRequest(array $request) array_diff_key($request['arguments'], ['filter' => 1, 'replacement' => 1]), ], ]; + case 'updateMany': case 'updateOne': return [ @@ -496,6 +515,7 @@ private function prepareBulkWriteRequest(array $request) array_diff_key($request['arguments'], ['filter' => 1, 'update' => 1]), ], ]; + default: throw new LogicException('Unsupported bulk write request: ' . $request['name']); } @@ -503,11 +523,8 @@ private function prepareBulkWriteRequest(array $request) /** * Prepares arguments for findOneAndReplace and findOneAndUpdate operations. - * - * @param array $arguments - * @return array */ - private function prepareFindAndModifyArguments(array $arguments) + private function prepareFindAndModifyArguments(array $arguments): array { if (isset($arguments['returnDocument'])) { $arguments['returnDocument'] = 'after' === strtolower($arguments['returnDocument']) diff --git a/tests/Collection/FunctionalTestCase.php b/tests/Collection/FunctionalTestCase.php index 1735b3ec8..d299d18fa 100644 --- a/tests/Collection/FunctionalTestCase.php +++ b/tests/Collection/FunctionalTestCase.php @@ -4,35 +4,19 @@ use MongoDB\Collection; use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; /** * Base class for Collection functional tests. */ abstract class FunctionalTestCase extends BaseFunctionalTestCase { - use SetUpTearDownTrait; - /** @var Collection */ protected $collection; - private function doSetUp() + public function setUp(): void { parent::setUp(); - $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); - - $this->dropCollection(); - } - - private function doTearDown() - { - if ($this->hasFailed()) { - return; - } - - $this->dropCollection(); - - parent::tearDown(); + $this->collection = $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); } } diff --git a/tests/Collection/spec-tests/read/aggregate-collation.json b/tests/Collection/spec-tests/read/aggregate-collation.json index 85662a442..d958e447b 100644 --- a/tests/Collection/spec-tests/read/aggregate-collation.json +++ b/tests/Collection/spec-tests/read/aggregate-collation.json @@ -6,6 +6,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "Aggregate with collation", diff --git a/tests/Collection/spec-tests/read/aggregate-out.json b/tests/Collection/spec-tests/read/aggregate-out.json index 205cf7657..c195e163e 100644 --- a/tests/Collection/spec-tests/read/aggregate-out.json +++ b/tests/Collection/spec-tests/read/aggregate-out.json @@ -14,6 +14,7 @@ } ], "minServerVersion": "2.6", + "serverless": "forbid", "tests": [ { "description": "Aggregate with $out", @@ -41,16 +42,6 @@ } }, "outcome": { - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], "collection": { "name": "other_test_collection", "data": [ @@ -92,16 +83,6 @@ } }, "outcome": { - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], "collection": { "name": "other_test_collection", "data": [ diff --git a/tests/Collection/spec-tests/read/count-collation.json b/tests/Collection/spec-tests/read/count-collation.json index 6f75282fe..7d6150849 100644 --- a/tests/Collection/spec-tests/read/count-collation.json +++ b/tests/Collection/spec-tests/read/count-collation.json @@ -6,6 +6,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "Count documents with collation", diff --git a/tests/Collection/spec-tests/read/distinct-collation.json b/tests/Collection/spec-tests/read/distinct-collation.json index 0af0c67cb..984991a43 100644 --- a/tests/Collection/spec-tests/read/distinct-collation.json +++ b/tests/Collection/spec-tests/read/distinct-collation.json @@ -10,6 +10,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "Distinct with a collation", diff --git a/tests/Collection/spec-tests/read/find-collation.json b/tests/Collection/spec-tests/read/find-collation.json index 53d0e9490..4e56c0525 100644 --- a/tests/Collection/spec-tests/read/find-collation.json +++ b/tests/Collection/spec-tests/read/find-collation.json @@ -6,6 +6,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "Find with a collation", diff --git a/tests/Collection/spec-tests/write/bulkWrite-collation.json b/tests/Collection/spec-tests/write/bulkWrite-collation.json index 8e9d1bcb1..bc90aa817 100644 --- a/tests/Collection/spec-tests/write/bulkWrite-collation.json +++ b/tests/Collection/spec-tests/write/bulkWrite-collation.json @@ -22,6 +22,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "BulkWrite with delete operations and collation", diff --git a/tests/Collection/spec-tests/write/bulkWrite.json b/tests/Collection/spec-tests/write/bulkWrite.json index 97879e7d3..dc00da28a 100644 --- a/tests/Collection/spec-tests/write/bulkWrite.json +++ b/tests/Collection/spec-tests/write/bulkWrite.json @@ -188,18 +188,6 @@ } } }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 11 - } - } - }, { "name": "replaceOne", "arguments": { @@ -234,11 +222,11 @@ "deletedCount": 0, "insertedCount": 0, "insertedIds": {}, - "matchedCount": 2, + "matchedCount": 1, "modifiedCount": 1, "upsertedCount": 1, "upsertedIds": { - "3": 3 + "2": 3 } }, "collection": { diff --git a/tests/Collection/spec-tests/write/deleteMany-collation.json b/tests/Collection/spec-tests/write/deleteMany-collation.json index d17bf3bcb..fce75e488 100644 --- a/tests/Collection/spec-tests/write/deleteMany-collation.json +++ b/tests/Collection/spec-tests/write/deleteMany-collation.json @@ -14,6 +14,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "DeleteMany when many documents match with collation", diff --git a/tests/Collection/spec-tests/write/deleteOne-collation.json b/tests/Collection/spec-tests/write/deleteOne-collation.json index 2f7f92113..9bcef411e 100644 --- a/tests/Collection/spec-tests/write/deleteOne-collation.json +++ b/tests/Collection/spec-tests/write/deleteOne-collation.json @@ -14,6 +14,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "DeleteOne when many documents matches with collation", diff --git a/tests/Collection/spec-tests/write/findOneAndDelete-collation.json b/tests/Collection/spec-tests/write/findOneAndDelete-collation.json index 1ff37d2e8..32480da84 100644 --- a/tests/Collection/spec-tests/write/findOneAndDelete-collation.json +++ b/tests/Collection/spec-tests/write/findOneAndDelete-collation.json @@ -14,6 +14,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "FindOneAndDelete when one document matches with collation", diff --git a/tests/Collection/spec-tests/write/findOneAndReplace-collation.json b/tests/Collection/spec-tests/write/findOneAndReplace-collation.json index babb2f7c1..9b3c25005 100644 --- a/tests/Collection/spec-tests/write/findOneAndReplace-collation.json +++ b/tests/Collection/spec-tests/write/findOneAndReplace-collation.json @@ -10,6 +10,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "FindOneAndReplace when one document matches with collation returning the document after modification", diff --git a/tests/Collection/spec-tests/write/findOneAndUpdate-collation.json b/tests/Collection/spec-tests/write/findOneAndUpdate-collation.json index 04c1fe73e..8abab7bd6 100644 --- a/tests/Collection/spec-tests/write/findOneAndUpdate-collation.json +++ b/tests/Collection/spec-tests/write/findOneAndUpdate-collation.json @@ -14,6 +14,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "FindOneAndUpdate when many documents match with collation returning the document before modification", diff --git a/tests/Collection/spec-tests/write/replaceOne-collation.json b/tests/Collection/spec-tests/write/replaceOne-collation.json index a668fe738..fa4cbe997 100644 --- a/tests/Collection/spec-tests/write/replaceOne-collation.json +++ b/tests/Collection/spec-tests/write/replaceOne-collation.json @@ -10,6 +10,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "ReplaceOne when one document matches with collation", diff --git a/tests/Collection/spec-tests/write/updateMany-collation.json b/tests/Collection/spec-tests/write/updateMany-collation.json index 3cb49f229..8becfd806 100644 --- a/tests/Collection/spec-tests/write/updateMany-collation.json +++ b/tests/Collection/spec-tests/write/updateMany-collation.json @@ -14,6 +14,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "UpdateMany when many documents match with collation", diff --git a/tests/Collection/spec-tests/write/updateOne-collation.json b/tests/Collection/spec-tests/write/updateOne-collation.json index c49112d51..3afdb83e0 100644 --- a/tests/Collection/spec-tests/write/updateOne-collation.json +++ b/tests/Collection/spec-tests/write/updateOne-collation.json @@ -10,6 +10,7 @@ } ], "minServerVersion": "3.4", + "serverless": "forbid", "tests": [ { "description": "UpdateOne when one document matches with collation", diff --git a/tests/Command/ListCollectionsTest.php b/tests/Command/ListCollectionsTest.php index 333152561..3cc5aa5e8 100644 --- a/tests/Command/ListCollectionsTest.php +++ b/tests/Command/ListCollectionsTest.php @@ -8,10 +8,8 @@ class ListCollectionsTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new ListCollections($this->getDatabaseName(), $options); @@ -21,6 +19,10 @@ public function provideInvalidConstructorOptions() { $options = []; + foreach ($this->getInvalidBooleanValues() as $value) { + $options[][] = ['authorizedCollections' => $value]; + } + foreach ($this->getInvalidDocumentValues() as $value) { $options[][] = ['filter' => $value]; } diff --git a/tests/Command/ListDatabasesTest.php b/tests/Command/ListDatabasesTest.php index d2ce95623..90a5513f1 100644 --- a/tests/Command/ListDatabasesTest.php +++ b/tests/Command/ListDatabasesTest.php @@ -8,10 +8,8 @@ class ListDatabasesTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new ListDatabases($options); diff --git a/tests/CommandObserver.php b/tests/CommandObserver.php index 44d0fa4ef..2f5165138 100644 --- a/tests/CommandObserver.php +++ b/tests/CommandObserver.php @@ -7,6 +7,7 @@ use MongoDB\Driver\Monitoring\CommandSubscriber; use MongoDB\Driver\Monitoring\CommandSucceededEvent; use Throwable; + use function call_user_func; use function MongoDB\Driver\Monitoring\addSubscriber; use function MongoDB\Driver\Monitoring\removeSubscriber; @@ -19,7 +20,7 @@ class CommandObserver implements CommandSubscriber /** @var array */ private $commands = []; - public function observe(callable $execution, callable $commandCallback) + public function observe(callable $execution, callable $commandCallback): void { $this->commands = []; @@ -41,17 +42,17 @@ public function observe(callable $execution, callable $commandCallback) } } - public function commandStarted(CommandStartedEvent $event) + public function commandStarted(CommandStartedEvent $event): void { $this->commands[$event->getRequestId()]['started'] = $event; } - public function commandSucceeded(CommandSucceededEvent $event) + public function commandSucceeded(CommandSucceededEvent $event): void { $this->commands[$event->getRequestId()]['succeeded'] = $event; } - public function commandFailed(CommandFailedEvent $event) + public function commandFailed(CommandFailedEvent $event): void { $this->commands[$event->getRequestId()]['failed'] = $event; } diff --git a/tests/Comparator/Int64Comparator.php b/tests/Comparator/Int64Comparator.php new file mode 100644 index 000000000..c985e0c03 --- /dev/null +++ b/tests/Comparator/Int64Comparator.php @@ -0,0 +1,45 @@ +isComparable($actual)) + || ($actual instanceof Int64 && $this->isComparable($expected)); + } + + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void + { + if ($expected == $actual) { + return; + } + + throw new ComparisonFailure( + $expected, + $actual, + '', + '', + false, + sprintf( + 'Failed asserting that %s matches expected %s.', + $this->exporter->export($actual), + $this->exporter->export($expected) + ) + ); + } + + private function isComparable($value): bool + { + return $value instanceof Int64 || is_numeric($value); + } +} diff --git a/tests/Comparator/Int64ComparatorTest.php b/tests/Comparator/Int64ComparatorTest.php new file mode 100644 index 000000000..9a887acd3 --- /dev/null +++ b/tests/Comparator/Int64ComparatorTest.php @@ -0,0 +1,211 @@ +assertSame($expectedResult, (new Int64Comparator())->accepts($expectedValue, $actualValue)); + } + + public static function provideAcceptsValues(): Generator + { + yield 'Expects Int64, Actual Int64' => [ + 'expectedResult' => true, + 'expectedValue' => new Int64(123), + 'actualValue' => new Int64(123), + ]; + + yield 'Expects Int64, Actual int' => [ + 'expectedResult' => true, + 'expectedValue' => new Int64(123), + 'actualValue' => 123, + ]; + + yield 'Expects Int64, Actual int string' => [ + 'expectedResult' => true, + 'expectedValue' => new Int64(123), + 'actualValue' => '123', + ]; + + yield 'Expects Int64, Actual float' => [ + 'expectedResult' => true, + 'expectedValue' => new Int64(123), + 'actualValue' => 123.0, + ]; + + yield 'Expects Int64, Actual float string' => [ + 'expectedResult' => true, + 'expectedValue' => new Int64(123), + 'actualValue' => '123.0', + ]; + + yield 'Expects Int64, Actual non-numeric string' => [ + 'expectedResult' => false, + 'expectedValue' => new Int64(123), + 'actualValue' => 'foo', + ]; + + yield 'Expects int, Actual Int64' => [ + 'expectedResult' => true, + 'expectedValue' => 123, + 'actualValue' => new Int64(123), + ]; + + yield 'Expects int string, Actual Int64' => [ + 'expectedResult' => true, + 'expectedValue' => '123', + 'actualValue' => new Int64(123), + ]; + + yield 'Expects float, Actual Int64' => [ + 'expectedResult' => true, + 'expectedValue' => 123.0, + 'actualValue' => new Int64(123), + ]; + + yield 'Expects float string, Actual Int64' => [ + 'expectedResult' => true, + 'expectedValue' => '123.0', + 'actualValue' => new Int64(123), + ]; + + yield 'Expects non-numeric string, Actual Int64' => [ + 'expectedResult' => false, + 'expectedValue' => 'foo', + 'actualValue' => new Int64(123), + ]; + + yield 'Expects float, Actual float' => [ + 'expectedResult' => false, + 'expectedValue' => 123.0, + 'actualValue' => 123.0, + ]; + + yield 'Expects numeric string, Actual numeric string' => [ + 'expectedResult' => false, + 'expectedValue' => '123', + 'actualValue' => '123', + ]; + } + + /** + * @dataProvider provideMatchingAssertions + * @doesNotPerformAssertions + */ + public function testMatchingAssertions($expected, $actual): void + { + (new Int64Comparator())->assertEquals($expected, $actual); + } + + public static function provideMatchingAssertions(): Generator + { + yield 'Expected Int64, Actual Int64' => [ + 'expected' => new Int64(8589934592), + 'actual' => new Int64(8589934592), + ]; + + yield 'Expected Int64, Actual int' => [ + 'expected' => new Int64(8589934592), + 'actual' => 8589934592, + ]; + + yield 'Expected Int64, Actual int string' => [ + 'expected' => new Int64(8589934592), + 'actual' => '8589934592', + ]; + + yield 'Expected Int64, Actual float' => [ + 'expected' => new Int64(8589934592), + 'actual' => 8589934592.0, + ]; + + yield 'Expected Int64, Actual float string' => [ + 'expected' => new Int64(8589934592), + 'actual' => '8589934592.0', + ]; + + yield 'Expected int, Actual Int64' => [ + 'expected' => 8589934592, + 'actual' => new Int64(8589934592), + ]; + + yield 'Expected int string, Actual Int64' => [ + 'expected' => '8589934592', + 'actual' => new Int64(8589934592), + ]; + + yield 'Expected float, Actual Int64' => [ + 'expected' => 8589934592.0, + 'actual' => new Int64(8589934592), + ]; + + yield 'Expected float string, Actual Int64' => [ + 'expected' => '8589934592.0', + 'actual' => new Int64(8589934592), + ]; + } + + /** @dataProvider provideFailingValues */ + public function testFailingAssertions($expected, $actual): void + { + $this->expectException(ComparisonFailure::class); + + (new Int64Comparator())->assertEquals($expected, $actual); + } + + public static function provideFailingValues(): Generator + { + yield 'Expected Int64, Actual Int64' => [ + 'expected' => new Int64(8589934592), + 'actual' => new Int64(456), + ]; + + yield 'Expected Int64, Actual int' => [ + 'expected' => new Int64(8589934592), + 'actual' => 456, + ]; + + yield 'Expected Int64, Actual int string' => [ + 'expected' => new Int64(8589934592), + 'actual' => '456', + ]; + + yield 'Expected Int64, Actual float' => [ + 'expected' => new Int64(8589934592), + 'actual' => 8589934592.1, + ]; + + yield 'Expected Int64, Actual float string' => [ + 'expected' => new Int64(8589934592), + 'actual' => '8589934592.1', + ]; + + yield 'Expected int, Actual Int64' => [ + 'expected' => 8589934592, + 'actual' => new Int64(456), + ]; + + yield 'Expected int string, Actual Int64' => [ + 'expected' => '8589934592', + 'actual' => new Int64(456), + ]; + + yield 'Expected float, Actual Int64' => [ + 'expected' => 8589934592.1, + 'actual' => new Int64(456), + ]; + + yield 'Expected float string, Actual Int64' => [ + 'expected' => '8589934592.1', + 'actual' => new Int64(456), + ]; + } +} diff --git a/tests/Database/CollectionManagementFunctionalTest.php b/tests/Database/CollectionManagementFunctionalTest.php index 246ceebb9..4cb5c6107 100644 --- a/tests/Database/CollectionManagementFunctionalTest.php +++ b/tests/Database/CollectionManagementFunctionalTest.php @@ -2,27 +2,23 @@ namespace MongoDB\Tests\Database; -use InvalidArgumentException; use MongoDB\Driver\BulkWrite; use MongoDB\Model\CollectionInfo; use MongoDB\Model\CollectionInfoIterator; -use function call_user_func; -use function is_callable; -use function sprintf; /** * Functional tests for collection management methods. */ class CollectionManagementFunctionalTest extends FunctionalTestCase { - public function testCreateCollection() + public function testCreateCollection(): void { $that = $this; $basicCollectionName = $this->getCollectionName() . '.basic'; $commandResult = $this->database->createCollection($basicCollectionName); $this->assertCommandSucceeded($commandResult); - $this->assertCollectionExists($basicCollectionName, function (CollectionInfo $info) use ($that) { + $this->assertCollectionExists($basicCollectionName, null, function (CollectionInfo $info) use ($that): void { $that->assertFalse($info->isCapped()); }); @@ -35,14 +31,14 @@ public function testCreateCollection() $commandResult = $this->database->createCollection($cappedCollectionName, $cappedCollectionOptions); $this->assertCommandSucceeded($commandResult); - $this->assertCollectionExists($cappedCollectionName, function (CollectionInfo $info) use ($that) { + $this->assertCollectionExists($cappedCollectionName, null, function (CollectionInfo $info) use ($that): void { $that->assertTrue($info->isCapped()); $that->assertEquals(100, $info->getCappedMax()); $that->assertEquals(1048576, $info->getCappedSize()); }); } - public function testDropCollection() + public function testDropCollection(): void { $bulkWrite = new BulkWrite(); $bulkWrite->insert(['x' => 1]); @@ -55,7 +51,7 @@ public function testDropCollection() $this->assertCollectionCount($this->getNamespace(), 0); } - public function testListCollections() + public function testListCollections(): void { $commandResult = $this->database->createCollection($this->getCollectionName()); $this->assertCommandSucceeded($commandResult); @@ -68,7 +64,7 @@ public function testListCollections() } } - public function testListCollectionsWithFilter() + public function testListCollectionsWithFilter(): void { $commandResult = $this->database->createCollection($this->getCollectionName()); $this->assertCommandSucceeded($commandResult); @@ -85,7 +81,7 @@ public function testListCollectionsWithFilter() } } - public function testListCollectionNames() + public function testListCollectionNames(): void { $commandResult = $this->database->createCollection($this->getCollectionName()); $this->assertCommandSucceeded($commandResult); @@ -97,7 +93,7 @@ public function testListCollectionNames() } } - public function testListCollectionNamesWithFilter() + public function testListCollectionNamesWithFilter(): void { $commandResult = $this->database->createCollection($this->getCollectionName()); $this->assertCommandSucceeded($commandResult); @@ -111,38 +107,4 @@ public function testListCollectionNamesWithFilter() $this->assertEquals($collectionName, $collection); } } - - /** - * Asserts that a collection with the given name exists in the database. - * - * An optional $callback may be provided, which should take a CollectionInfo - * argument as its first and only parameter. If a CollectionInfo matching - * the given name is found, it will be passed to the callback, which may - * perform additional assertions. - * - * @param callable $callback - */ - private function assertCollectionExists($collectionName, $callback = null) - { - if ($callback !== null && ! is_callable($callback)) { - throw new InvalidArgumentException('$callback is not a callable'); - } - - $collections = $this->database->listCollections(); - - $foundCollection = null; - - foreach ($collections as $collection) { - if ($collection->getName() === $collectionName) { - $foundCollection = $collection; - break; - } - } - - $this->assertNotNull($foundCollection, sprintf('Found %s collection in the database', $collectionName)); - - if ($callback !== null) { - call_user_func($callback, $foundCollection); - } - } } diff --git a/tests/Database/DatabaseFunctionalTest.php b/tests/Database/DatabaseFunctionalTest.php index b913697b2..d1f74025c 100644 --- a/tests/Database/DatabaseFunctionalTest.php +++ b/tests/Database/DatabaseFunctionalTest.php @@ -2,6 +2,7 @@ namespace MongoDB\Tests\Database; +use MongoDB\Collection; use MongoDB\Database; use MongoDB\Driver\BulkWrite; use MongoDB\Driver\Cursor; @@ -10,6 +11,8 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\CreateIndexes; +use TypeError; + use function array_key_exists; use function current; @@ -18,12 +21,10 @@ */ class DatabaseFunctionalTest extends FunctionalTestCase { - /** - * @dataProvider provideInvalidDatabaseNames - */ - public function testConstructorDatabaseNameArgument($databaseName) + /** @dataProvider provideInvalidDatabaseNames */ + public function testConstructorDatabaseNameArgument($databaseName, string $expectedExceptionClass): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($expectedExceptionClass); // TODO: Move to unit test once ManagerInterface can be mocked (PHPC-378) new Database($this->manager, $databaseName); } @@ -31,15 +32,13 @@ public function testConstructorDatabaseNameArgument($databaseName) public function provideInvalidDatabaseNames() { return [ - [null], - [''], + [null, TypeError::class], + ['', InvalidArgumentException::class], ]; } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Database($this->manager, $this->getDatabaseName(), $options); @@ -68,26 +67,26 @@ public function provideInvalidConstructorOptions() return $options; } - public function testGetManager() + public function testGetManager(): void { $this->assertSame($this->manager, $this->database->getManager()); } - public function testToString() + public function testToString(): void { $this->assertEquals($this->getDatabaseName(), (string) $this->database); } - public function getGetDatabaseName() + public function getGetDatabaseName(): void { $this->assertEquals($this->getDatabaseName(), $this->database->getDatabaseName()); } - public function testCommand() + public function testCommand(): void { - $command = ['isMaster' => 1]; + $command = ['ping' => 1]; $options = [ - 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), ]; $cursor = $this->database->command($command, $options); @@ -96,17 +95,17 @@ public function testCommand() $commandResult = current($cursor->toArray()); $this->assertCommandSucceeded($commandResult); - $this->assertObjectHasAttribute('ismaster', $commandResult); - $this->assertTrue($commandResult->ismaster); + $this->assertObjectHasAttribute('ok', $commandResult); + $this->assertSame(1, (int) $commandResult->ok); } - public function testCommandDoesNotInheritReadPreference() + public function testCommandDoesNotInheritReadPreference(): void { if (! $this->isReplicaSet()) { $this->markTestSkipped('Test only applies to replica sets'); } - $this->database = new Database($this->manager, $this->getDatabaseName(), ['readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY)]); + $this->database = new Database($this->manager, $this->getDatabaseName(), ['readPreference' => new ReadPreference(ReadPreference::SECONDARY)]); $command = ['ping' => 1]; @@ -116,11 +115,11 @@ public function testCommandDoesNotInheritReadPreference() $this->assertTrue($cursor->getServer()->isPrimary()); } - public function testCommandAppliesTypeMapToCursor() + public function testCommandAppliesTypeMapToCursor(): void { - $command = ['isMaster' => 1]; + $command = ['ping' => 1]; $options = [ - 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), 'typeMap' => ['root' => 'array'], ]; @@ -131,20 +130,18 @@ public function testCommandAppliesTypeMapToCursor() $this->assertCommandSucceeded($commandResult); $this->assertIsArray($commandResult); - $this->assertArrayHasKey('ismaster', $commandResult); - $this->assertTrue($commandResult['ismaster']); + $this->assertArrayHasKey('ok', $commandResult); + $this->assertSame(1, (int) $commandResult['ok']); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testCommandCommandArgumentTypeCheck($command) + /** @dataProvider provideInvalidDocumentValues */ + public function testCommandCommandArgumentTypeCheck($command): void { $this->expectException(InvalidArgumentException::class); $this->database->command($command); } - public function testDrop() + public function testDrop(): void { $bulkWrite = new BulkWrite(); $bulkWrite->insert(['x' => 1]); @@ -157,7 +154,20 @@ public function testDrop() $this->assertCollectionCount($this->getNamespace(), 0); } - public function testGetSelectsCollectionAndInheritsOptions() + public function testDropCollection(): void + { + $bulkWrite = new BulkWrite(); + $bulkWrite->insert(['x' => 1]); + + $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $commandResult = $this->database->dropCollection($this->getCollectionName()); + $this->assertCommandSucceeded($commandResult); + $this->assertCollectionDoesNotExist($this->getCollectionName()); + } + + public function testGetSelectsCollectionAndInheritsOptions(): void { $databaseOptions = ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]; @@ -172,7 +182,12 @@ public function testGetSelectsCollectionAndInheritsOptions() $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testModifyCollection() + /** + * @group matrix-testing-exclude-server-4.2-driver-4.0-topology-sharded_cluster + * @group matrix-testing-exclude-server-4.4-driver-4.0-topology-sharded_cluster + * @group matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster + */ + public function testModifyCollection(): void { $this->database->createCollection($this->getCollectionName()); @@ -205,11 +220,73 @@ public function testModifyCollection() } } - public function testSelectCollectionInheritsOptions() + public function testRenameCollectionToSameDatabase(): void + { + $toCollectionName = $this->getCollectionName() . '.renamed'; + $toCollection = new Collection($this->manager, $this->getDatabaseName(), $toCollectionName); + + $bulkWrite = new BulkWrite(); + $bulkWrite->insert(['_id' => 1]); + + $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $commandResult = $this->database->renameCollection( + $this->getCollectionName(), + $toCollectionName, + null, + ['dropTarget' => true] + ); + $this->assertCommandSucceeded($commandResult); + $this->assertCollectionDoesNotExist($this->getCollectionName()); + $this->assertCollectionExists($toCollectionName); + + $this->assertSameDocument(['_id' => 1], $toCollection->findOne()); + $toCollection->drop(); + } + + public function testRenameCollectionToDifferentDatabase(): void + { + $toDatabaseName = $this->getDatabaseName() . '_renamed'; + $toDatabase = new Database($this->manager, $toDatabaseName); + + /* When renaming an unsharded collection, mongos requires the source + * and target database to both exist on the primary shard. In practice, this + * means we need to create the target database explicitly. + * See: https://mongodb.com/docs/manual/reference/command/renameCollection/#unsharded-collections + */ + if ($this->isShardedCluster()) { + $toDatabase->foo->insertOne(['_id' => 1]); + } + + $toCollectionName = $this->getCollectionName() . '.renamed'; + $toCollection = new Collection($this->manager, $toDatabaseName, $toCollectionName); + + $bulkWrite = new BulkWrite(); + $bulkWrite->insert(['_id' => 1]); + + $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $commandResult = $this->database->renameCollection( + $this->getCollectionName(), + $toCollectionName, + $toDatabaseName + ); + $this->assertCommandSucceeded($commandResult); + $this->assertCollectionDoesNotExist($this->getCollectionName()); + $this->assertCollectionExists($toCollectionName, $toDatabaseName); + + $this->assertSameDocument(['_id' => 1], $toCollection->findOne()); + + $toDatabase->drop(); + } + + public function testSelectCollectionInheritsOptions(): void { $databaseOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -224,18 +301,18 @@ public function testSelectCollectionInheritsOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testSelectCollectionPassesOptions() + public function testSelectCollectionPassesOptions(): void { $collectionOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -246,18 +323,18 @@ public function testSelectCollectionPassesOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testSelectGridFSBucketInheritsOptions() + public function testSelectGridFSBucketInheritsOptions(): void { $databaseOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -272,18 +349,18 @@ public function testSelectGridFSBucketInheritsOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testSelectGridFSBucketPassesOptions() + public function testSelectGridFSBucketPassesOptions(): void { $bucketOptions = [ 'bucketName' => 'custom_fs', 'chunkSizeBytes' => 8192, 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -297,16 +374,16 @@ public function testSelectGridFSBucketPassesOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testWithOptionsInheritsOptions() + public function testWithOptionsInheritsOptions(): void { $databaseOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -320,18 +397,18 @@ public function testWithOptionsInheritsOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - public function testWithOptionsPassesOptions() + public function testWithOptionsPassesOptions(): void { $databaseOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -342,7 +419,7 @@ public function testWithOptionsPassesOptions() $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); diff --git a/tests/Database/FunctionalTestCase.php b/tests/Database/FunctionalTestCase.php index 186df2239..5e0906731 100644 --- a/tests/Database/FunctionalTestCase.php +++ b/tests/Database/FunctionalTestCase.php @@ -4,19 +4,16 @@ use MongoDB\Database; use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; /** * Base class for Database functional tests. */ abstract class FunctionalTestCase extends BaseFunctionalTestCase { - use SetUpTearDownTrait; - /** @var Database */ protected $database; - private function doSetUp() + public function setUp(): void { parent::setUp(); diff --git a/tests/DocumentationExamplesTest.php b/tests/DocumentationExamplesTest.php index c59058ce9..001057454 100644 --- a/tests/DocumentationExamplesTest.php +++ b/tests/DocumentationExamplesTest.php @@ -2,20 +2,24 @@ namespace MongoDB\Tests; +use MongoDB\BSON\Binary; use MongoDB\BSON\ObjectId; use MongoDB\BSON\UTCDateTime; -use MongoDB\Client; +use MongoDB\Collection; use MongoDB\Database; use MongoDB\Driver\Cursor; -use MongoDB\Driver\Exception\ConnectionTimeoutException; +use MongoDB\Driver\Exception\CommandException; +use MongoDB\Driver\Exception\Exception; use MongoDB\Driver\ReadPreference; -use MongoDB\Driver\WriteConcern; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; +use MongoDB\Tests\SpecTests\ClientSideEncryptionSpecTest; + +use function base64_decode; use function in_array; +use function microtime; use function ob_end_clean; use function ob_start; +use function usleep; use function var_dump; -use function version_compare; /** * Documentation examples to be parsed for inclusion in the MongoDB manual. @@ -26,28 +30,9 @@ */ class DocumentationExamplesTest extends FunctionalTestCase { - use SetUpTearDownTrait; - - private function doSetUp() - { - parent::setUp(); - - $this->dropCollection(); - } - - private function doTearDown() - { - if ($this->hasFailed()) { - return; - } - - $this->dropCollection(); - - parent::tearDown(); - } - - public function testExample_1_2() + public function testExample_1_2(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 1 @@ -70,8 +55,9 @@ public function testExample_1_2() $this->assertCursorCount(1, $cursor); } - public function testExample_3() + public function testExample_3(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 3 @@ -101,11 +87,13 @@ public function testExample_3() foreach ($insertManyResult->getInsertedIds() as $id) { $this->assertInstanceOf(ObjectId::class, $id); } + $this->assertInventoryCount(3); } - public function testExample_6_13() + public function testExample_6_13(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 6 @@ -147,6 +135,7 @@ public function testExample_6_13() foreach ($insertManyResult->getInsertedIds() as $id) { $this->assertInstanceOf(ObjectId::class, $id); } + $this->assertInventoryCount(5); // Start Example 7 @@ -207,8 +196,9 @@ public function testExample_6_13() $this->assertCursorCount(2, $cursor); } - public function testExample_14_19() + public function testExample_14_19(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 14 @@ -250,6 +240,7 @@ public function testExample_14_19() foreach ($insertManyResult->getInsertedIds() as $id) { $this->assertInstanceOf(ObjectId::class, $id); } + $this->assertInventoryCount(5); // Start Example 15 @@ -287,8 +278,9 @@ public function testExample_14_19() $this->assertCursorCount(1, $cursor); } - public function testExample_20_28() + public function testExample_20_28(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 20 @@ -330,6 +322,7 @@ public function testExample_20_28() foreach ($insertManyResult->getInsertedIds() as $id) { $this->assertInstanceOf(ObjectId::class, $id); } + $this->assertInventoryCount(5); // Start Example 21 @@ -393,8 +386,9 @@ public function testExample_20_28() $this->assertCursorCount(1, $cursor); } - public function testExample_29_37() + public function testExample_29_37(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 29 @@ -440,6 +434,7 @@ public function testExample_29_37() foreach ($insertManyResult->getInsertedIds() as $id) { $this->assertInstanceOf(ObjectId::class, $id); } + $this->assertInventoryCount(5); // Start Example 30 @@ -491,8 +486,9 @@ public function testExample_29_37() $this->assertCursorCount(2, $cursor); } - public function testExample_38_41() + public function testExample_38_41(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 38 @@ -506,6 +502,7 @@ public function testExample_38_41() foreach ($insertManyResult->getInsertedIds() as $id) { $this->assertIsInt($id); } + $this->assertInventoryCount(2); // Start Example 39 @@ -527,8 +524,9 @@ public function testExample_38_41() $this->assertCursorCount(1, $cursor); } - public function testExample_42_50() + public function testExample_42_50(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 42 @@ -581,6 +579,7 @@ public function testExample_42_50() foreach ($insertManyResult->getInsertedIds() as $id) { $this->assertInstanceOf(ObjectId::class, $id); } + $this->assertInventoryCount(5); // Start Example 43 @@ -608,6 +607,7 @@ public function testExample_42_50() foreach (['_id', 'item', 'status'] as $field) { $this->assertObjectHasAttribute($field, $document); } + foreach (['size', 'instock'] as $field) { $this->assertObjectNotHasAttribute($field, $document); } @@ -626,6 +626,7 @@ public function testExample_42_50() foreach (['item', 'status'] as $field) { $this->assertObjectHasAttribute($field, $document); } + foreach (['_id', 'size', 'instock'] as $field) { $this->assertObjectNotHasAttribute($field, $document); } @@ -644,6 +645,7 @@ public function testExample_42_50() foreach (['_id', 'item', 'size'] as $field) { $this->assertObjectHasAttribute($field, $document); } + foreach (['status', 'instock'] as $field) { $this->assertObjectNotHasAttribute($field, $document); } @@ -662,6 +664,7 @@ public function testExample_42_50() foreach (['_id', 'item', 'status', 'size'] as $field) { $this->assertObjectHasAttribute($field, $document); } + $this->assertObjectNotHasAttribute('instock', $document); $this->assertObjectHasAttribute('uom', $document->size); $this->assertObjectNotHasAttribute('h', $document->size); @@ -681,6 +684,7 @@ public function testExample_42_50() foreach (['_id', 'item', 'status', 'size', 'instock'] as $field) { $this->assertObjectHasAttribute($field, $document); } + $this->assertObjectHasAttribute('h', $document->size); $this->assertObjectHasAttribute('w', $document->size); $this->assertObjectNotHasAttribute('uom', $document->size); @@ -699,6 +703,7 @@ public function testExample_42_50() foreach (['_id', 'item', 'status', 'instock'] as $field) { $this->assertObjectHasAttribute($field, $document); } + $this->assertObjectNotHasAttribute('size', $document); foreach ($document->instock as $instock) { $this->assertObjectHasAttribute('qty', $instock); @@ -719,13 +724,15 @@ public function testExample_42_50() foreach (['_id', 'item', 'status', 'instock'] as $field) { $this->assertObjectHasAttribute($field, $document); } + $this->assertObjectNotHasAttribute('size', $document); $this->assertCount(1, $document->instock); } } - public function testExample_51_54() + public function testExample_51_54(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 51 @@ -797,6 +804,7 @@ public function testExample_51_54() foreach ($insertManyResult->getInsertedIds() as $id) { $this->assertInstanceOf(ObjectId::class, $id); } + $this->assertInventoryCount(10); // Start Example 52 @@ -864,8 +872,9 @@ public function testExample_51_54() $this->assertCursorCount(1, $cursor); } - public function testExample_55_58() + public function testExample_55_58(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 55 @@ -907,6 +916,7 @@ public function testExample_55_58() foreach ($insertManyResult->getInsertedIds() as $id) { $this->assertInstanceOf(ObjectId::class, $id); } + $this->assertInventoryCount(5); // Start Example 57 @@ -933,7 +943,8 @@ public function testExample_55_58() $this->assertInventoryCount(0); } - public function testChangeStreamExample_1_4() + /** @group matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster */ + public function testChangeStreamExample_1_4(): void { $this->skipIfChangeStreamIsNotSupported(); @@ -941,9 +952,8 @@ public function testChangeStreamExample_1_4() $this->markTestSkipped('Test does not apply on sharded clusters: need more than a single getMore call on the change stream.'); } + $this->createCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); - $db->dropCollection('inventory'); - $db->createCollection('inventory'); // Start Changestream Example 1 $changeStream = $db->inventory->watch(); @@ -1039,8 +1049,9 @@ public function testChangeStreamExample_1_4() $this->assertNull($secondChange); } - public function testAggregation_example_1() + public function testAggregation_example_1(): void { + $this->dropCollection($this->getDatabaseName(), 'sales'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Aggregation Example 1 @@ -1053,8 +1064,9 @@ public function testAggregation_example_1() $this->assertInstanceOf(Cursor::class, $cursor); } - public function testAggregation_example_2() + public function testAggregation_example_2(): void { + $this->dropCollection($this->getDatabaseName(), 'sales'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Aggregation Example 2 @@ -1081,8 +1093,9 @@ public function testAggregation_example_2() $this->assertInstanceOf(Cursor::class, $cursor); } - public function testAggregation_example_3() + public function testAggregation_example_3(): void { + $this->dropCollection($this->getDatabaseName(), 'sales'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Aggregation Example 3 @@ -1119,14 +1132,12 @@ public function testAggregation_example_3() $this->assertInstanceOf(Cursor::class, $cursor); } - public function testAggregation_example_4() + public function testAggregation_example_4(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('$lookup does not support "let" option'); - } - + $this->dropCollection($this->getDatabaseName(), 'air_airlines'); $db = new Database($this->manager, $this->getDatabaseName()); + // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps // Start Aggregation Example 4 $cursor = $db->air_alliances->aggregate([ [ @@ -1157,11 +1168,12 @@ public function testAggregation_example_4() ], ]); // End Aggregation Example 4 + // phpcs:enable $this->assertInstanceOf(Cursor::class, $cursor); } - public function testRunCommand_example_1() + public function testRunCommand_example_1(): void { $db = new Database($this->manager, $this->getDatabaseName()); @@ -1173,22 +1185,9 @@ public function testRunCommand_example_1() $this->assertInstanceOf(Cursor::class, $cursor); } - public function testRunCommand_example_2() - { - $db = new Database($this->manager, $this->getDatabaseName()); - $db->dropCollection('restaurants'); - $db->createCollection('restaurants'); - - // Start runCommand Example 2 - $cursor = $db->command(['collStats' => 'restaurants']); - $result = $cursor->toArray()[0]; - // End runCommand Example 2 - - $this->assertInstanceOf(Cursor::class, $cursor); - } - - public function testIndex_example_1() + public function testIndex_example_1(): void { + $this->dropCollection($this->getDatabaseName(), 'records'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Index Example 1 @@ -1198,8 +1197,9 @@ public function testIndex_example_1() $this->assertEquals('score_1', $indexName); } - public function testIndex_example_2() + public function testIndex_example_2(): void { + $this->dropCollection($this->getDatabaseName(), 'restaurants'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Index Example 2 @@ -1216,7 +1216,7 @@ public function testIndex_example_2() // phpcs:disable Squiz.Commenting.FunctionComment.WrongStyle // phpcs:disable Squiz.WhiteSpace.FunctionSpacing.After // Start Transactions Intro Example 1 - private function updateEmployeeInfo1(\MongoDB\Client $client, \MongoDB\Driver\Session $session) + private function updateEmployeeInfo1(\MongoDB\Client $client, \MongoDB\Driver\Session $session): void { $session->startTransaction([ 'readConcern' => new \MongoDB\Driver\ReadConcern('snapshot'), @@ -1230,12 +1230,13 @@ private function updateEmployeeInfo1(\MongoDB\Client $client, \MongoDB\Driver\Se ['session' => $session] ); $client->reporting->events->insertOne( - ['employee' => 3, 'status' => [ 'new' => 'Inactive', 'old' => 'Active']], + ['employee' => 3, 'status' => ['new' => 'Inactive', 'old' => 'Active']], ['session' => $session] ); } catch (\MongoDB\Driver\Exception\Exception $error) { echo "Caught exception during transaction, aborting.\n"; $session->abortTransaction(); + throw $error; } @@ -1252,10 +1253,12 @@ private function updateEmployeeInfo1(\MongoDB\Client $client, \MongoDB\Driver\Se continue; } else { echo "Error during commit ...\n"; + throw $error; } } catch (\MongoDB\Driver\Exception\Exception $error) { echo "Error during commit ...\n"; + throw $error; } } @@ -1263,21 +1266,17 @@ private function updateEmployeeInfo1(\MongoDB\Client $client, \MongoDB\Driver\Se // End Transactions Intro Example 1 // phpcs:enable - public function testTransactions_intro_example_1() + public function testTransactions_intro_example_1(): void { $this->skipIfTransactionsAreNotSupported(); $this->assertNotNull('This test intentionally performs no assertions'); - $client = new Client(static::getUri()); - - /* The WC is required: https://docs.mongodb.com/manual/core/transactions/#transactions-and-locks */ - $client->hr->dropCollection('employees', ['writeConcern' => new WriteConcern('majority')]); - $client->reporting->dropCollection('events', ['writeConcern' => new WriteConcern('majority')]); + $client = static::createTestClient(); /* Collections need to be created before a transaction starts */ - $client->hr->createCollection('employees', ['writeConcern' => new WriteConcern('majority')]); - $client->reporting->createCollection('events', ['writeConcern' => new WriteConcern('majority')]); + $this->createCollection('hr', 'employees'); + $this->createCollection('reporting', 'events'); $session = $client->startSession(); @@ -1293,7 +1292,7 @@ public function testTransactions_intro_example_1() // phpcs:disable Squiz.Commenting.FunctionComment.WrongStyle // phpcs:disable Squiz.WhiteSpace.FunctionSpacing.After // Start Transactions Retry Example 1 - private function runTransactionWithRetry1(callable $txnFunc, \MongoDB\Client $client, \MongoDB\Driver\Session $session) + private function runTransactionWithRetry1(callable $txnFunc, \MongoDB\Client $client, \MongoDB\Driver\Session $session): void { while (true) { try { @@ -1322,7 +1321,7 @@ private function runTransactionWithRetry1(callable $txnFunc, \MongoDB\Client $cl // phpcs:disable Squiz.Commenting.FunctionComment.WrongStyle // phpcs:disable Squiz.WhiteSpace.FunctionSpacing.After // Start Transactions Retry Example 2 - private function commitWithRetry2(\MongoDB\Driver\Session $session) + private function commitWithRetry2(\MongoDB\Driver\Session $session): void { while (true) { try { @@ -1337,10 +1336,12 @@ private function commitWithRetry2(\MongoDB\Driver\Session $session) continue; } else { echo "Error during commit ...\n"; + throw $error; } } catch (\MongoDB\Driver\Exception\Exception $error) { echo "Error during commit ...\n"; + throw $error; } } @@ -1352,7 +1353,7 @@ private function commitWithRetry2(\MongoDB\Driver\Session $session) // phpcs:disable Squiz.Commenting.FunctionComment.WrongStyle // phpcs:disable Squiz.WhiteSpace.FunctionSpacing.After // Start Transactions Retry Example 3 - private function runTransactionWithRetry3(callable $txnFunc, \MongoDB\Client $client, \MongoDB\Driver\Session $session) + private function runTransactionWithRetry3(callable $txnFunc, \MongoDB\Client $client, \MongoDB\Driver\Session $session): void { while (true) { try { @@ -1373,7 +1374,7 @@ private function runTransactionWithRetry3(callable $txnFunc, \MongoDB\Client $cl } } - private function commitWithRetry3(\MongoDB\Driver\Session $session) + private function commitWithRetry3(\MongoDB\Driver\Session $session): void { while (true) { try { @@ -1388,20 +1389,22 @@ private function commitWithRetry3(\MongoDB\Driver\Session $session) continue; } else { echo "Error during commit ...\n"; + throw $error; } } catch (\MongoDB\Driver\Exception\Exception $error) { echo "Error during commit ...\n"; + throw $error; } } } - private function updateEmployeeInfo3(\MongoDB\Client $client, \MongoDB\Driver\Session $session) + private function updateEmployeeInfo3(\MongoDB\Client $client, \MongoDB\Driver\Session $session): void { $session->startTransaction([ 'readConcern' => new \MongoDB\Driver\ReadConcern("snapshot"), - 'readPrefernece' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::RP_PRIMARY), + 'readPrefernece' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::PRIMARY), 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY), ]); @@ -1412,19 +1415,20 @@ private function updateEmployeeInfo3(\MongoDB\Client $client, \MongoDB\Driver\Se ['session' => $session] ); $client->reporting->events->insertOne( - ['employee' => 3, 'status' => [ 'new' => 'Inactive', 'old' => 'Active']], + ['employee' => 3, 'status' => ['new' => 'Inactive', 'old' => 'Active']], ['session' => $session] ); } catch (\MongoDB\Driver\Exception\Exception $error) { echo "Caught exception during transaction, aborting.\n"; $session->abortTransaction(); + throw $error; } $this->commitWithRetry3($session); } - private function doUpdateEmployeeInfo(\MongoDB\Client $client) + private function doUpdateEmployeeInfo(\MongoDB\Client $client): void { // Start a session. $session = $client->startSession(); @@ -1438,21 +1442,17 @@ private function doUpdateEmployeeInfo(\MongoDB\Client $client) // End Transactions Retry Example 3 // phpcs:enable - public function testTransactions_retry_example_3() + public function testTransactions_retry_example_3(): void { $this->skipIfTransactionsAreNotSupported(); $this->assertNotNull('This test intentionally performs no assertions'); - $client = new Client(static::getUri()); - - /* The WC is required: https://docs.mongodb.com/manual/core/transactions/#transactions-and-locks */ - $client->hr->dropCollection('employees', ['writeConcern' => new WriteConcern('majority')]); - $client->reporting->dropCollection('events', ['writeConcern' => new WriteConcern('majority')]); + $client = static::createTestClient(); /* Collections need to be created before a transaction starts */ - $client->hr->createCollection('employees', ['writeConcern' => new WriteConcern('majority')]); - $client->reporting->createCollection('events', ['writeConcern' => new WriteConcern('majority')]); + $this->createCollection('hr', 'employees'); + $this->createCollection('reporting', 'events'); ob_start(); try { @@ -1462,30 +1462,29 @@ public function testTransactions_retry_example_3() } } - public function testCausalConsistency() + public function testCausalConsistency(): void { $this->skipIfCausalConsistencyIsNotSupported(); - try { - $this->manager->selectServer(new ReadPreference('secondary')); - } catch (ConnectionTimeoutException $e) { - $this->markTestSkipped('Secondary is not available'); - } - $this->assertNotNull('This test intentionally performs no assertions'); // Prep - $client = new Client(static::getUri()); - $items = $client->selectDatabase( - 'test', - [ 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY) ] - )->items; - - $items->drop(); + $client = static::createTestClient(); + $items = $this->createCollection('test', 'items'); $items->insertOne( - [ 'sku' => '111', 'name' => 'Peanuts', 'start' => new UTCDateTime() ] + ['sku' => '111', 'name' => 'Peanuts', 'start' => new UTCDateTime()] ); + try { + /* In sharded clusters, server selection ignores the read preference + * mode, so using $manager->selectServer does not work here. To work + * around this, we run a query on a secondary and rely on an + * exception to let us know that no secondary is available. */ + $items->countDocuments([], ['readPreference' => new ReadPreference(ReadPreference::SECONDARY)]); + } catch (Exception $e) { + $this->markTestSkipped('Secondary is not available'); + } + // phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly // Start Causal Consistency Example 1 $items = $client->selectDatabase( @@ -1497,19 +1496,19 @@ public function testCausalConsistency() )->items; $s1 = $client->startSession( - [ 'causalConsistency' => true ] + ['causalConsistency' => true] ); $currentDate = new \MongoDB\BSON\UTCDateTime(); $items->updateOne( - [ 'sku' => '111', 'end' => [ '$exists' => false ] ], - [ '$set' => [ 'end' => $currentDate ] ], - [ 'session' => $s1 ] + ['sku' => '111', 'end' => ['$exists' => false]], + ['$set' => ['end' => $currentDate]], + ['session' => $s1] ); $items->insertOne( - [ 'sku' => '111-nuts', 'name' => 'Pecans', 'start' => $currentDate ], - [ 'session' => $s1 ] + ['sku' => '111-nuts', 'name' => 'Pecans', 'start' => $currentDate], + ['session' => $s1] ); // End Causal Consistency Example 1 // phpcs:enable @@ -1519,7 +1518,7 @@ public function testCausalConsistency() // phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly // Start Causal Consistency Example 2 $s2 = $client->startSession( - [ 'causalConsistency' => true ] + ['causalConsistency' => true] ); $s2->advanceClusterTime($s1->getClusterTime()); $s2->advanceOperationTime($s1->getOperationTime()); @@ -1527,33 +1526,224 @@ public function testCausalConsistency() $items = $client->selectDatabase( 'test', [ - 'readPreference' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::RP_SECONDARY), + 'readPreference' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::SECONDARY), 'readConcern' => new \MongoDB\Driver\ReadConcern(\MongoDB\Driver\ReadConcern::MAJORITY), 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000), ] )->items; $result = $items->find( - [ 'end' => [ '$exists' => false ] ], - [ 'session' => $s2 ] + ['end' => ['$exists' => false]], + ['session' => $s2] ); foreach ($result as $item) { var_dump($item); } + // End Causal Consistency Example 2 // phpcs:enable ob_end_clean(); } - /** - * @doesNotPerformAssertions - */ - public function testWithTransactionExample() + public function testSnapshotQueries(): void + { + $this->skipIfServerVersion('<', '5.0.0', 'Snapshot queries outside of transactions are not supported'); + + if (! ($this->isReplicaSet() || $this->isShardedClusterUsingReplicasets())) { + $this->markTestSkipped('Snapshot read concern is only supported with replicasets'); + } + + $catsCollection = $this->dropCollection('pets', 'cats'); + $catsCollection->insertMany([ + ['name' => 'Whiskers', 'color' => 'white', 'adoptable' => true], + ['name' => 'Garfield', 'color' => 'orange', 'adoptable' => false], + ]); + + $dogsCollection = $this->dropCollection('pets', 'dogs'); + $dogsCollection->insertMany([ + ['name' => 'Toto', 'color' => 'black', 'adoptable' => true], + ['name' => 'Milo', 'color' => 'black', 'adoptable' => false], + ['name' => 'Brian', 'color' => 'white', 'adoptable' => true], + ]); + + if ($this->isShardedCluster()) { + $this->preventStaleDbVersionError('pets', 'cats'); + $this->preventStaleDbVersionError('pets', 'dogs'); + } else { + $this->waitForSnapshot('pets', 'cats'); + $this->waitForSnapshot('pets', 'dogs'); + } + + $client = static::createTestClient(); + + ob_start(); + + // Start Snapshot Query Example 1 + $catsCollection = $client->selectCollection('pets', 'cats'); + $dogsCollection = $client->selectCollection('pets', 'dogs'); + + $session = $client->startSession(['snapshot' => true]); + + $adoptablePetsCount = $catsCollection->aggregate( + [ + ['$match' => ['adoptable' => true]], + ['$count' => 'adoptableCatsCount'], + ], + ['session' => $session] + )->toArray()[0]->adoptableCatsCount; + + $adoptablePetsCount += $dogsCollection->aggregate( + [ + ['$match' => ['adoptable' => true]], + ['$count' => 'adoptableDogsCount'], + ], + ['session' => $session] + )->toArray()[0]->adoptableDogsCount; + + var_dump($adoptablePetsCount); + // End Snapshot Query Example 1 + + ob_end_clean(); + + $this->assertSame(3, $adoptablePetsCount); + + $salesCollection = $this->dropCollection('retail', 'sales'); + $salesCollection->insertMany([ + ['shoeType' => 'boot', 'price' => 30, 'saleDate' => new UTCDateTime()], + ]); + + if ($this->isShardedCluster()) { + $this->preventStaleDbVersionError('retail', 'sales'); + } else { + $this->waitForSnapshot('retail', 'sales'); + } + + // Start Snapshot Query Example 2 + $salesCollection = $client->selectCollection('retail', 'sales'); + + $session = $client->startSession(['snapshot' => true]); + + $totalDailySales = $salesCollection->aggregate( + [ + [ + '$match' => [ + '$expr' => [ + '$gt' => ['$saleDate', [ + '$dateSubtract' => [ + 'startDate' => '$$NOW', + 'unit' => 'day', + 'amount' => 1, + ], + ], + ], + ], + ], + ], + ['$count' => 'totalDailySales'], + ], + ['session' => $session] + )->toArray()[0]->totalDailySales; + // End Snapshot Query Example 2 + + $this->assertSame(1, $totalDailySales); + } + + /** @doesNotPerformAssertions */ + public function testVersionedApi(): void + { + $uriString = static::getUri(true); + + // phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly + // Start Versioned API Example 1 + $serverApi = new \MongoDB\Driver\ServerApi('1'); + $client = new \MongoDB\Client($uriString, [], ['serverApi' => $serverApi]); + // End Versioned API Example 1 + + // Start Versioned API Example 2 + $serverApi = new \MongoDB\Driver\ServerApi('1', true); + $client = new \MongoDB\Client($uriString, [], ['serverApi' => $serverApi]); + // End Versioned API Example 2 + + // Start Versioned API Example 3 + $serverApi = new \MongoDB\Driver\ServerApi('1', false); + $client = new \MongoDB\Client($uriString, [], ['serverApi' => $serverApi]); + // End Versioned API Example 3 + + // Start Versioned API Example 4 + $serverApi = new \MongoDB\Driver\ServerApi('1', false, true); + $client = new \MongoDB\Client($uriString, [], ['serverApi' => $serverApi]); + // End Versioned API Example 4 + // phpcs:enable + } + + public function testVersionedApiMigration(): void + { + $this->skipIfServerVersion('<', '5.0.0', 'Versioned API is not supported'); + $this->skipIfServerVersion('>=', '5.0.9', 'The count command was added to API version 1 (SERVER-63850)'); + + $this->dropCollection($this->getDatabaseName(), 'sales'); + $uriString = static::getUri(true); + + // phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly + $serverApi = new \MongoDB\Driver\ServerApi('1', true); + $client = new \MongoDB\Client($uriString, [], ['serverApi' => $serverApi]); + $db = $client->selectDatabase($this->getDatabaseName()); + + // Start Versioned API Example 5 + $strtoutc = function (string $datetime) { + return new \MongoDB\BSON\UTCDateTime(new \DateTime($datetime)); + }; + + $db->sales->insertMany([ + ['_id' => 1, 'item' => 'abc', 'price' => 10, 'quantity' => 2, 'date' => $strtoutc('2021-01-01T08:00:00Z')], + ['_id' => 2, 'item' => 'jkl', 'price' => 20, 'quantity' => 1, 'date' => $strtoutc('2021-02-03T09:00:00Z')], + ['_id' => 3, 'item' => 'xyz', 'price' => 5, 'quantity' => 5, 'date' => $strtoutc('2021-02-03T09:05:00Z')], + ['_id' => 4, 'item' => 'abc', 'price' => 10, 'quantity' => 10, 'date' => $strtoutc('2021-02-15T08:00:00Z')], + ['_id' => 5, 'item' => 'xyz', 'price' => 5, 'quantity' => 10, 'date' => $strtoutc('2021-02-15T09:05:00Z')], + ['_id' => 6, 'item' => 'xyz', 'price' => 5, 'quantity' => 5, 'date' => $strtoutc('2021-02-15T12:05:10Z')], + ['_id' => 7, 'item' => 'xyz', 'price' => 5, 'quantity' => 10, 'date' => $strtoutc('2021-02-15T14:12:12Z')], + ['_id' => 8, 'item' => 'abc', 'price' => 10, 'quantity' => 5, 'date' => $strtoutc('2021-03-16T20:20:13Z')], + ]); + // End Versioned API Example 5 + + ob_start(); + + // Start Versioned API Example 6 + try { + $count = $db->sales->count(); + } catch (\MongoDB\Driver\Exception\CommandException $e) { + echo json_encode($e->getResultDocument()); + // { "ok": 0, "errmsg": "Provided apiStrict:true, but the command count is not in API Version 1", "code": 323, "codeName": "APIStrictError" } + } + + // End Versioned API Example 6 + + ob_end_clean(); + + $this->assertStringContainsString('Provided apiStrict:true, but the command count is not in API Version 1', $e->getMessage()); + $this->assertEquals(323 /* APIStrictError */, $e->getCode()); + + // Start Versioned API Example 7 + $count = $db->sales->countDocuments(); + // End Versioned API Example 7 + + $this->assertSame($count, $db->sales->countDocuments()); + + // Start Versioned API Example 8 + // 8 + // End Versioned API Example 8 + // phpcs:enable + } + + /** @doesNotPerformAssertions */ + public function testWithTransactionExample(): void { $this->skipIfTransactionsAreNotSupported(); $uriString = static::getUri(true); + $this->dropCollection('mydb1', 'foo'); + $this->dropCollection('mydb2', 'bar'); // phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly // Start Transactions withTxn API Example 1 @@ -1585,7 +1775,7 @@ public function testWithTransactionExample() // Step 1: Define the callback that specifies the sequence of operations to perform inside the transactions. - $callback = function (\MongoDB\Driver\Session $session) use ($client) { + $callback = function (\MongoDB\Driver\Session $session) use ($client): void { $client ->selectCollection('mydb1', 'foo') ->insertOne(['abc' => 1], ['session' => $session]); @@ -1601,35 +1791,152 @@ public function testWithTransactionExample() // Step 3: Use with_transaction to start a transaction, execute the callback, and commit (or abort on error). - $transactionOptions = [ - 'readConcern' => new \MongoDB\Driver\ReadConcern(\MongoDB\Driver\ReadConcern::LOCAL), - 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000), - 'readPreference' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::RP_PRIMARY), - ]; - - \MongoDB\with_transaction($session, $callback, $transactionOptions); + \MongoDB\with_transaction($session, $callback); // End Transactions withTxn API Example 1 // phpcs:enable } /** - * Return the test collection name. + * Queryable encryption examples (not parsed for server manual includes). * - * @return string + * @see https://jira.mongodb.org/browse/PHPLIB-863 + * @see ClientSideEncryptionSpecTest::testExplicitEncryption */ - protected function getCollectionName() + public function testQueryableEncryption(): void { - return 'inventory'; + if ($this->isStandalone() || ($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets())) { + $this->markTestSkipped('Queryable encryption requires replica sets'); + } + + $this->skipIfServerVersion('<', '7.0.0', 'Explicit encryption tests require MongoDB 7.0 or later'); + + if (! $this->isEnterprise()) { + $this->markTestSkipped('Automatic encryption requires MongoDB Enterprise'); + } + + // Fetch names for the database and collection under test + $collectionName = $this->getCollectionName(); + $databaseName = $this->getDatabaseName(); + $namespace = $this->getNamespace(); + + /* Create a client without auto encryption. Drop existing data in both the + * keyvault and the collection under test, the "encryptedFields" option + * is necessary to clean up any internal collections for queryable + * encryption, assuming they use their default names */ + $client = static::createTestClient(); + $this->dropCollection('keyvault', 'datakeys'); + $this->dropCollection($databaseName, $collectionName, ['encryptedFields' => []]); + + /* Although ClientEncryption can be constructed directly, the library + * provides a helper to do so. With this method, the keyVaultClient will + * default to the same client. */ + $clientEncryption = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(ClientSideEncryptionSpecTest::LOCAL_MASTERKEY), 0)]], + ]); + + // Create two data keys, one for each encrypted field + $dataKeyId1 = $clientEncryption->createDataKey('local'); + $dataKeyId2 = $clientEncryption->createDataKey('local'); + + $autoEncryptionOpts = [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(ClientSideEncryptionSpecTest::LOCAL_MASTERKEY), 0)]], + 'encryptedFieldsMap' => [ + $namespace => [ + 'fields' => [ + [ + 'path' => 'encryptedIndexed', + 'bsonType' => 'string', + 'keyId' => $dataKeyId1, + 'queries' => ['queryType' => 'equality'], + ], + [ + 'path' => 'encryptedUnindexed', + 'bsonType' => 'string', + 'keyId' => $dataKeyId2, + ], + ], + ], + ], + ]; + + $encryptedClient = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); + + /* Create the collection under test. The createCollection() helper will + * reference the client's encryptedFieldsMap and create additional, + * internal collections automatically. */ + $encryptedClient->selectDatabase($databaseName)->createCollection($collectionName); + $encryptedCollection = $encryptedClient->selectCollection($databaseName, $collectionName); + + /* Using a client with auto encryption, insert a document with encrypted + * fields and assert that those fields are automatically decrypted when + * querying. */ + $indexedValue = 'indexedValue'; + $unindexedValue = 'unindexedValue'; + + $encryptedCollection->insertOne([ + '_id' => 1, + 'encryptedIndexed' => $indexedValue, + 'encryptedUnindexed' => $unindexedValue, + ]); + + $result = $encryptedCollection->findOne(['encryptedIndexed' => $indexedValue]); + + $this->assertSame(1, $result['_id']); + $this->assertSame($indexedValue, $result['encryptedIndexed']); + $this->assertSame($unindexedValue, $result['encryptedUnindexed']); + + /* Using a client without auto encryption, query for the same + * document and assert that encrypted data is returned. */ + $unencryptedClient = static::createTestClient(); + $unencryptedCollection = $unencryptedClient->selectCollection($databaseName, $collectionName); + + $result = $unencryptedCollection->findOne(['_id' => 1]); + + $this->assertSame(1, $result['_id']); + $this->assertInstanceOf(Binary::class, $result['encryptedIndexed']); + $this->assertInstanceOf(Binary::class, $result['encryptedUnindexed']); } - private function assertCursorCount($count, Cursor $cursor) + private function assertCursorCount($count, Cursor $cursor): void { $this->assertCount($count, $cursor->toArray()); } - private function assertInventoryCount($count) + private function assertInventoryCount($count): void + { + $this->assertCollectionCount($this->getDatabaseName() . '.inventory', $count); + } + + private function waitForSnapshot(string $databaseName, string $collectionName): void + { + $collection = new Collection($this->manager, $databaseName, $collectionName); + $session = $this->manager->startSession(['snapshot' => true]); + + // Retry until a snapshot query succeeds or ten seconds elapse. + $retryUntil = microtime(true) + 10; + + do { + try { + if ($collection->findOne([], ['session' => $session]) !== null) { + break; + } + } catch (CommandException $e) { + if ($e->getCode() !== 246 /* SnapshotUnavailable */) { + throw $e; + } + } + + usleep(1000); + } while (microtime(true) < $retryUntil); + } + + /** @see https://jira.mongodb.org/browse/SERVER-39704 */ + private function preventStaleDbVersionError(string $databaseName, string $collectionName): void { - $this->assertCollectionCount($this->getDatabaseName() . '.' . $this->getCollectionName(), $count); + $collection = new Collection($this->manager, $databaseName, $collectionName); + $collection->distinct('foo'); } } diff --git a/tests/ExamplesTest.php b/tests/ExamplesTest.php new file mode 100644 index 000000000..997bc3312 --- /dev/null +++ b/tests/ExamplesTest.php @@ -0,0 +1,200 @@ +isShardedCluster()) { + $this->markTestSkipped('Examples are not tested on sharded clusters.'); + } + + if ($this->isApiVersionRequired()) { + $this->markTestSkipped('Examples are not tested with when the server requires specifying an API version.'); + } + + self::createTestClient()->dropDatabase('test'); + } + + public function dataExamples(): Generator + { + $expectedOutput = <<<'OUTPUT' +{ "_id" : null, "totalCount" : 100, "evenCount" : %d, "oddCount" : %d, "maxValue" : %d, "minValue" : %d } +OUTPUT; + + yield 'aggregate' => [ + 'file' => __DIR__ . '/../examples/aggregate.php', + 'expectedOutput' => $expectedOutput, + ]; + + $expectedOutput = <<<'OUTPUT' +%s +%s +%s +%s +%s +%s +%s +%s +OUTPUT; + + yield 'bulk' => [ + 'file' => __DIR__ . '/../examples/bulk.php', + 'expectedOutput' => $expectedOutput, + ]; + + $expectedOutput = <<<'OUTPUT' +drop command started +command: %s + +drop command %a + +insert command started +command: %s + +insert command succeeded +reply: %s + +update command started +command: %s + +update command succeeded +reply: %s + +find command started +command: %s + +find command succeeded +reply: %s + +%s +%s +getMore command started +command: %s + +getMore command succeeded +reply: %s + +%s +OUTPUT; + + yield 'command_logger' => [ + 'file' => __DIR__ . '/../examples/command_logger.php', + 'expectedOutput' => $expectedOutput, + ]; + + $expectedOutput = <<<'OUTPUT' +MongoDB\Examples\PersistableEntry Object +( + [id:MongoDB\Examples\PersistableEntry:private] => MongoDB\BSON\ObjectId Object + ( + [oid] => %s + ) + + [name] => alcaeus + [emails] => Array + ( + [0] => MongoDB\Examples\PersistableEmail Object + ( + [type] => work + [address] => alcaeus@example.com + ) + + [1] => MongoDB\Examples\PersistableEmail Object + ( + [type] => private + [address] => secret@example.com + ) + + ) + +) +OUTPUT; + + yield 'persistable' => [ + 'file' => __DIR__ . '/../examples/persistable.php', + 'expectedOutput' => $expectedOutput, + ]; + + $expectedOutput = <<<'OUTPUT' +MongoDB\Examples\TypeMapEntry Object +( + [id:MongoDB\Examples\TypeMapEntry:private] => MongoDB\BSON\ObjectId Object + ( + [oid] => %s + ) + + [name:MongoDB\Examples\TypeMapEntry:private] => alcaeus + [emails:MongoDB\Examples\TypeMapEntry:private] => Array + ( + [0] => MongoDB\Examples\TypeMapEmail Object + ( + [type:MongoDB\Examples\TypeMapEmail:private] => work + [address:MongoDB\Examples\TypeMapEmail:private] => alcaeus@example.com + ) + + [1] => MongoDB\Examples\TypeMapEmail Object + ( + [type:MongoDB\Examples\TypeMapEmail:private] => private + [address:MongoDB\Examples\TypeMapEmail:private] => secret@example.com + ) + + ) + +) +OUTPUT; + + yield 'typemap' => [ + 'file' => __DIR__ . '/../examples/typemap.php', + 'expectedOutput' => $expectedOutput, + ]; + } + + public function testChangeStream(): void + { + $this->skipIfChangeStreamIsNotSupported(); + + $expectedOutput = <<<'OUTPUT' +%s +%s +%s +%s +%s +%s +%s +%s +%s +%s +Aborting after 3 seconds... +OUTPUT; + + $this->testExample(__DIR__ . '/../examples/changestream.php', $expectedOutput); + } + + /** @dataProvider dataExamples */ + public function testExample(string $file, string $expectedOutput): void + { + require $file; + + self::assertStringMatchesFormat($expectedOutput, $this->getActualOutputForAssertion()); + } + + public function testWithTransaction(): void + { + $this->skipIfTransactionsAreNotSupported(); + + $expectedOutput = <<<'OUTPUT' +%s +%s +%s +OUTPUT; + + $this->testExample(__DIR__ . '/../examples/with_transaction.php', $expectedOutput); + } +} diff --git a/tests/Exception/CreateEncryptedCollectionExceptionTest.php b/tests/Exception/CreateEncryptedCollectionExceptionTest.php new file mode 100644 index 000000000..b48273493 --- /dev/null +++ b/tests/Exception/CreateEncryptedCollectionExceptionTest.php @@ -0,0 +1,26 @@ + []]; + + $e = new CreateEncryptedCollectionException(new Exception(), $encryptedFields); + $this->assertSame($encryptedFields, $e->getEncryptedFields()); + } + + public function testGetPrevious(): void + { + $previous = new Exception(); + + $e = new CreateEncryptedCollectionException($previous, ['fields' => []]); + $this->assertSame($previous, $e->getPrevious()); + } +} diff --git a/tests/Exception/InvalidArgumentExceptionTest.php b/tests/Exception/InvalidArgumentExceptionTest.php new file mode 100644 index 000000000..c4ab90a1d --- /dev/null +++ b/tests/Exception/InvalidArgumentExceptionTest.php @@ -0,0 +1,46 @@ +assertStringContainsString($typeString, $e->getMessage()); + } + + public function provideExpectedTypes() + { + yield 'expectedType is a string' => [ + 'array', + 'type "array"', + ]; + + yield 'expectedType is an array with one string' => [ + ['array'], + 'type "array"', + ]; + + yield 'expectedType is an array with two strings' => [ + ['array', 'integer'], + 'type "array" or "integer"', + ]; + + yield 'expectedType is an array with three strings' => [ + ['array', 'integer', 'object'], + 'type "array", "integer", or "object"', + ]; + } + + public function testExpectedTypeArrayMustNotBeEmpty(): void + { + $this->expectException(AssertionError::class); + InvalidArgumentException::invalidType('$arg', null, []); + } +} diff --git a/tests/FunctionalTestCase.php b/tests/FunctionalTestCase.php index 03d389677..b4d4fb5ab 100644 --- a/tests/FunctionalTestCase.php +++ b/tests/FunctionalTestCase.php @@ -4,25 +4,33 @@ use InvalidArgumentException; use MongoDB\BSON\ObjectId; +use MongoDB\Client; +use MongoDB\Collection; use MongoDB\Driver\Command; use MongoDB\Driver\Exception\CommandException; use MongoDB\Driver\Manager; use MongoDB\Driver\Query; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\Server; +use MongoDB\Driver\ServerApi; use MongoDB\Driver\WriteConcern; use MongoDB\Operation\CreateCollection; use MongoDB\Operation\DatabaseCommand; -use MongoDB\Operation\DropCollection; +use MongoDB\Operation\ListCollections; use stdClass; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; use UnexpectedValueException; -use function array_merge; + +use function array_intersect_key; +use function call_user_func; use function count; use function current; use function explode; +use function filter_var; +use function getenv; use function implode; +use function in_array; use function is_array; +use function is_callable; use function is_object; use function is_string; use function key; @@ -32,108 +40,153 @@ use function phpinfo; use function preg_match; use function preg_quote; +use function preg_replace; use function sprintf; use function version_compare; + +use const FILTER_VALIDATE_BOOLEAN; use const INFO_MODULES; abstract class FunctionalTestCase extends TestCase { - use SetUpTearDownTrait; - /** @var Manager */ protected $manager; /** @var array */ private $configuredFailPoints = []; - private function doSetUp() + /** @var array{int,{Collection,array}} */ + private $collectionsToCleanup = []; + + public function setUp(): void { parent::setUp(); - $this->manager = new Manager(static::getUri()); + $this->manager = static::createTestManager(); $this->configuredFailPoints = []; } - private function doTearDown() + public function tearDown(): void { + if (! $this->hasFailed()) { + $this->cleanupCollections(); + } + $this->disableFailPoints(); parent::tearDown(); } - public static function getUri($allowMultipleMongoses = false) + public static function createTestClient(?string $uri = null, array $options = [], array $driverOptions = []): Client { - $uri = parent::getUri(); + return new Client( + $uri ?? static::getUri(), + static::appendAuthenticationOptions($options), + static::appendServerApiOption($driverOptions) + ); + } + public static function createTestManager(?string $uri = null, array $options = [], array $driverOptions = []): Manager + { + return new Manager( + $uri ?? static::getUri(), + static::appendAuthenticationOptions($options), + static::appendServerApiOption($driverOptions) + ); + } + + public static function getUri($allowMultipleMongoses = false): string + { + /* If multiple mongoses are allowed, the multi-mongos load balanced URI + * can be used if available; otherwise, fall back MONGODB_URI. */ if ($allowMultipleMongoses) { - return $uri; + return getenv('MONGODB_MULTI_MONGOS_LB_URI') ?: parent::getUri(); } - $urlParts = parse_url($uri); - if ($urlParts === false) { - return $uri; - } + /* If multiple mongoses are prohibited, the single-mongos load balanced + * URI can be used if available; otherwise, we need to conditionally + * process MONGODB_URI. */ + return getenv('MONGODB_SINGLE_MONGOS_LB_URI') ?: static::getUriWithoutMultipleMongoses(); + } - // Only modify URIs using the mongodb scheme - if ($urlParts['scheme'] !== 'mongodb') { - return $uri; - } + protected function assertCollectionCount($namespace, $count): void + { + [$databaseName, $collectionName] = explode('.', $namespace, 2); - $hosts = explode(',', $urlParts['host']); - $numHosts = count($hosts); - if ($numHosts === 1) { - return $uri; - } + $cursor = $this->manager->executeCommand($databaseName, new Command(['count' => $collectionName])); + $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); + $document = current($cursor->toArray()); - $manager = new Manager($uri); - if ($manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY))->getType() !== Server::TYPE_MONGOS) { - return $uri; - } + $this->assertArrayHasKey('n', $document); + $this->assertEquals($count, $document['n']); + } - // Re-append port to last host - if (isset($urlParts['port'])) { - $hosts[$numHosts-1] .= ':' . $urlParts['port']; + /** + * Asserts that a collection with the given name does not exist on the + * server. + * + * $databaseName defaults to TestCase::getDatabaseName() if unspecified. + */ + protected function assertCollectionDoesNotExist(string $collectionName, ?string $databaseName = null): void + { + if (! isset($databaseName)) { + $databaseName = $this->getDatabaseName(); } - $parts = ['mongodb://']; + $operation = new ListCollections($this->getDatabaseName()); + $collections = $operation->execute($this->getPrimaryServer()); - if (isset($urlParts['user'], $urlParts['pass'])) { - $parts += [ - $urlParts['user'], - ':', - $urlParts['pass'], - '@', - ]; + $foundCollection = null; + + foreach ($collections as $collection) { + if ($collection->getName() === $collectionName) { + $foundCollection = $collection; + break; + } } - $parts[] = $hosts[0]; + $this->assertNull($foundCollection, sprintf('Collection %s exists', $collectionName)); + } - if (isset($urlParts['path'])) { - $parts[] = $urlParts['path']; + /** + * Asserts that a collection with the given name exists on the server. + * + * $databaseName defaults to TestCase::getDatabaseName() if unspecified. + * An optional $callback may be provided, which should take a CollectionInfo + * argument as its first and only parameter. If a CollectionInfo matching + * the given name is found, it will be passed to the callback, which may + * perform additional assertions. + */ + protected function assertCollectionExists(string $collectionName, ?string $databaseName = null, ?callable $callback = null): void + { + if (! isset($databaseName)) { + $databaseName = $this->getDatabaseName(); } - if (isset($urlParts['query'])) { - $parts = array_merge($parts, [ - '?', - $urlParts['query'], - ]); + + if ($callback !== null && ! is_callable($callback)) { + throw new InvalidArgumentException('$callback is not a callable'); } - return implode('', $parts); - } + $operation = new ListCollections($databaseName); + $collections = $operation->execute($this->getPrimaryServer()); - protected function assertCollectionCount($namespace, $count) - { - list($databaseName, $collectionName) = explode('.', $namespace, 2); + $foundCollection = null; - $cursor = $this->manager->executeCommand($databaseName, new Command(['count' => $collectionName])); - $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); - $document = current($cursor->toArray()); + foreach ($collections as $collection) { + if ($collection->getName() === $collectionName) { + $foundCollection = $collection; + break; + } + } - $this->assertArrayHasKey('n', $document); - $this->assertEquals($count, $document['n']); + $this->assertNotNull($foundCollection, sprintf('Found %s collection in the database', $collectionName)); + + if ($callback !== null) { + call_user_func($callback, $foundCollection); + } } - protected function assertCommandSucceeded($document) + protected function assertCommandSucceeded($document): void { $document = is_object($document) ? (array) $document : $document; @@ -141,7 +194,7 @@ protected function assertCommandSucceeded($document) $this->assertEquals(1, $document['ok']); } - protected function assertSameObjectId($expectedObjectId, $actualObjectId) + protected function assertSameObjectId($expectedObjectId, $actualObjectId): void { $this->assertInstanceOf(ObjectId::class, $expectedObjectId); $this->assertInstanceOf(ObjectId::class, $actualObjectId); @@ -157,7 +210,7 @@ protected function assertSameObjectId($expectedObjectId, $actualObjectId) * @param array|stdClass $command configureFailPoint command document * @throws InvalidArgumentException if $command is not a configureFailPoint command */ - public function configureFailPoint($command, Server $server = null) + public function configureFailPoint($command, ?Server $server = null): void { if (! $this->isFailCommandSupported()) { $this->markTestSkipped('failCommand is only supported on mongod >= 4.0.0 and mongos >= 4.1.5.'); @@ -175,7 +228,7 @@ public function configureFailPoint($command, Server $server = null) throw new InvalidArgumentException('$command is not an array or stdClass instance'); } - if (key($command) !== 'configureFailPoint') { + if (key((array) $command) !== 'configureFailPoint') { throw new InvalidArgumentException('$command is not a configureFailPoint command'); } @@ -191,105 +244,128 @@ public function configureFailPoint($command, Server $server = null) $this->configuredFailPoints[] = [$command->configureFailPoint, $failPointServer]; } + public static function getModuleInfo(string $row): ?string + { + ob_start(); + phpinfo(INFO_MODULES); + $info = ob_get_clean(); + + $pattern = sprintf('/^%s(.*)$/m', preg_quote($row . ' => ')); + + if (preg_match($pattern, $info, $matches) !== 1) { + return null; + } + + return $matches[1]; + } + /** - * Creates the test collection with the specified options. + * Creates the test collection with the specified options and ensures it is + * dropped again during tearDown(). If the collection already exists, it + * is dropped and recreated. * - * If the "writeConcern" option is not specified but is supported by the - * server, a majority write concern will be used. This is helpful for tests - * using transactions or secondary reads. + * A majority write concern is applied by default to ensure that the + * transaction can acquire the required locks. + * See: https://www.mongodb.com/docs/manual/core/transactions/#transactions-and-operations * - * @param array $options + * @param array $options CreateCollection options */ - protected function createCollection(array $options = []) + protected function createCollection(string $databaseName, string $collectionName, array $options = []): Collection { - if (version_compare($this->getServerVersion(), '3.4.0', '>=')) { - $options += ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]; + // See: https://jira.mongodb.org/browse/PHPLIB-1145 + if (isset($options['encryptedFields'])) { + throw new InvalidArgumentException('The "encryptedFields" option is not supported by createCollection(). Time to refactor!'); } - $operation = new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), $options); + // Pass only relevant options to drop the collection in case it already exists + $dropOptions = array_intersect_key($options, ['writeConcern' => 1, 'encryptedFields' => 1]); + $collection = $this->dropCollection($databaseName, $collectionName, $dropOptions); + + $options += ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]; + $operation = new CreateCollection($databaseName, $collectionName, $options); $operation->execute($this->getPrimaryServer()); + + return $collection; } /** - * Drops the test collection with the specified options. + * Drops the test collection and ensures it is dropped again during tearDown(). * - * If the "writeConcern" option is not specified but is supported by the - * server, a majority write concern will be used. This is helpful for tests - * using transactions or secondary reads. + * A majority write concern is applied by default to ensure that the + * transaction can acquire the required locks. + * See: https://www.mongodb.com/docs/manual/core/transactions/#transactions-and-operations * - * @param array $options + * @param array $options Collection::dropCollection() options */ - protected function dropCollection(array $options = []) + protected function dropCollection(string $databaseName, string $collectionName, array $options = []): Collection { - if (version_compare($this->getServerVersion(), '3.4.0', '>=')) { + $collection = new Collection($this->manager, $databaseName, $collectionName); + $this->collectionsToCleanup[] = [$collection, $options]; + + $options += ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]; + $collection->drop($options); + + return $collection; + } + + private function cleanupCollections(): void + { + foreach ($this->collectionsToCleanup as [$collection, $options]) { $options += ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]; + $collection->drop($options); } - $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName(), $options); - $operation->execute($this->getPrimaryServer()); + $this->collectionsToCleanup = []; } - protected function getFeatureCompatibilityVersion(ReadPreference $readPreference = null) + protected function getFeatureCompatibilityVersion(?ReadPreference $readPreference = null) { if ($this->isShardedCluster()) { return $this->getServerVersion($readPreference); } - if (version_compare($this->getServerVersion(), '3.4.0', '<')) { - return $this->getServerVersion($readPreference); - } - $cursor = $this->manager->executeCommand( 'admin', new Command(['getParameter' => 1, 'featureCompatibilityVersion' => 1]), - $readPreference ?: new ReadPreference(ReadPreference::RP_PRIMARY) + $readPreference ?: new ReadPreference(ReadPreference::PRIMARY) ); $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); $document = current($cursor->toArray()); - // MongoDB 3.6: featureCompatibilityVersion is an embedded document if (isset($document['featureCompatibilityVersion']['version']) && is_string($document['featureCompatibilityVersion']['version'])) { return $document['featureCompatibilityVersion']['version']; } - // MongoDB 3.4: featureCompatibilityVersion is a string - if (isset($document['featureCompatibilityVersion']) && is_string($document['featureCompatibilityVersion'])) { - return $document['featureCompatibilityVersion']; - } - throw new UnexpectedValueException('Could not determine featureCompatibilityVersion'); } protected function getPrimaryServer() { - return $this->manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY)); + return $this->manager->selectServer(); } - protected function getServerVersion(ReadPreference $readPreference = null) + protected function getServerVersion(?ReadPreference $readPreference = null) { - $cursor = $this->manager->executeCommand( + $buildInfo = $this->manager->executeCommand( $this->getDatabaseName(), new Command(['buildInfo' => 1]), - $readPreference ?: new ReadPreference(ReadPreference::RP_PRIMARY) - ); - - $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); - $document = current($cursor->toArray()); + $readPreference ?: new ReadPreference(ReadPreference::PRIMARY) + )->toArray()[0]; - if (isset($document['version']) && is_string($document['version'])) { - return $document['version']; + if (isset($buildInfo->version) && is_string($buildInfo->version)) { + return preg_replace('#^(\d+\.\d+\.\d+).*$#', '\1', $buildInfo->version); } throw new UnexpectedValueException('Could not determine server version'); } - protected function getServerStorageEngine(ReadPreference $readPreference = null) + protected function getServerStorageEngine(?ReadPreference $readPreference = null) { $cursor = $this->manager->executeCommand( $this->getDatabaseName(), new Command(['serverStatus' => 1]), - $readPreference ?: new ReadPreference('primary') + $readPreference ?: new ReadPreference(ReadPreference::PRIMARY) ); $result = current($cursor->toArray()); @@ -301,18 +377,93 @@ protected function getServerStorageEngine(ReadPreference $readPreference = null) throw new UnexpectedValueException('Could not determine server storage engine'); } + /** + * Returns whether clients must specify an API version by checking the + * requireApiVersion server parameter. + */ + protected function isApiVersionRequired(): bool + { + try { + $cursor = $this->manager->executeCommand( + 'admin', + new Command(['getParameter' => 1, 'requireApiVersion' => 1]) + ); + + $document = current($cursor->toArray()); + } catch (CommandException $e) { + return false; + } + + return isset($document->requireApiVersion) && $document->requireApiVersion === true; + } + + protected function isEnterprise(): bool + { + $buildInfo = $this->getPrimaryServer()->executeCommand( + $this->getDatabaseName(), + new Command(['buildInfo' => 1]) + )->toArray()[0]; + + if (isset($buildInfo->modules) && is_array($buildInfo->modules)) { + return in_array('enterprise', $buildInfo->modules); + } + + throw new UnexpectedValueException('Could not determine server modules'); + } + + protected function isLoadBalanced() + { + return $this->getPrimaryServer()->getType() == Server::TYPE_LOAD_BALANCER; + } + protected function isReplicaSet() { return $this->getPrimaryServer()->getType() == Server::TYPE_RS_PRIMARY; } - protected function isShardedCluster() + protected function isMongos() { return $this->getPrimaryServer()->getType() == Server::TYPE_MONGOS; } + protected function isStandalone() + { + return $this->getPrimaryServer()->getType() == Server::TYPE_STANDALONE; + } + + /** + * Return whether serverless (i.e. proxy as mongos) is being utilized. + */ + protected static function isServerless(): bool + { + $isServerless = getenv('MONGODB_IS_SERVERLESS'); + + return $isServerless !== false ? filter_var($isServerless, FILTER_VALIDATE_BOOLEAN) : false; + } + + protected function isShardedCluster() + { + $type = $this->getPrimaryServer()->getType(); + + if ($type == Server::TYPE_MONGOS) { + return true; + } + + // Assume that load balancers are properly configured and front sharded clusters + if ($type == Server::TYPE_LOAD_BALANCER) { + return true; + } + + return false; + } + protected function isShardedClusterUsingReplicasets() { + // Assume serverless is a sharded cluster using replica sets + if (static::isServerless()) { + return true; + } + $cursor = $this->getPrimaryServer()->executeQuery( 'config.shards', new Query([], ['limit' => 1]) @@ -333,22 +484,25 @@ protected function isShardedClusterUsingReplicasets() return preg_match('@^.*/.*:\d+@', $document['host']); } - protected function skipIfChangeStreamIsNotSupported() + protected function skipIfServerVersion(string $operator, string $version, ?string $message = null): void + { + if (version_compare($this->getServerVersion(), $version, $operator)) { + $this->markTestSkipped($message ?? sprintf('Server version is %s %s', $operator, $version)); + } + } + + protected function skipIfChangeStreamIsNotSupported(): void { switch ($this->getPrimaryServer()->getType()) { case Server::TYPE_MONGOS: - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('$changeStream is only supported on MongoDB 3.6 or higher'); - } + case Server::TYPE_LOAD_BALANCER: if (! $this->isShardedClusterUsingReplicasets()) { $this->markTestSkipped('$changeStream is only supported with replicasets'); } + break; case Server::TYPE_RS_PRIMARY: - if (version_compare($this->getFeatureCompatibilityVersion(), '3.6', '<')) { - $this->markTestSkipped('$changeStream is only supported on FCV 3.6 or higher'); - } break; default: @@ -356,25 +510,22 @@ protected function skipIfChangeStreamIsNotSupported() } } - protected function skipIfCausalConsistencyIsNotSupported() + protected function skipIfCausalConsistencyIsNotSupported(): void { switch ($this->getPrimaryServer()->getType()) { case Server::TYPE_MONGOS: - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Causal Consistency is only supported on MongoDB 3.6 or higher'); - } + case Server::TYPE_LOAD_BALANCER: if (! $this->isShardedClusterUsingReplicasets()) { $this->markTestSkipped('Causal Consistency is only supported with replicasets'); } + break; case Server::TYPE_RS_PRIMARY: - if (version_compare($this->getFeatureCompatibilityVersion(), '3.6', '<')) { - $this->markTestSkipped('Causal Consistency is only supported on FCV 3.6 or higher'); - } if ($this->getServerStorageEngine() !== 'wiredTiger') { $this->markTestSkipped('Causal Consistency requires WiredTiger storage engine'); } + break; default: @@ -382,18 +533,25 @@ protected function skipIfCausalConsistencyIsNotSupported() } } - protected function skipIfClientSideEncryptionIsNotSupported() + protected function skipIfClientSideEncryptionIsNotSupported(): void { if (version_compare($this->getFeatureCompatibilityVersion(), '4.2', '<')) { $this->markTestSkipped('Client Side Encryption only supported on FCV 4.2 or higher'); } - if ($this->getModuleInfo('libmongocrypt') === 'disabled') { + if (static::getModuleInfo('libmongocrypt') === 'disabled') { $this->markTestSkipped('Client Side Encryption is not enabled in the MongoDB extension'); } } - protected function skipIfTransactionsAreNotSupported() + protected function skipIfGeoHaystackIndexIsNotSupported(): void + { + if (version_compare($this->getServerVersion(), '4.9', '>=')) { + $this->markTestSkipped('GeoHaystack indexes cannot be created in version 4.9 and above'); + } + } + + protected function skipIfTransactionsAreNotSupported(): void { if ($this->getPrimaryServer()->getType() === Server::TYPE_STANDALONE) { $this->markTestSkipped('Transactions are not supported on standalone servers'); @@ -420,50 +578,121 @@ protected function skipIfTransactionsAreNotSupported() } } + private static function appendAuthenticationOptions(array $options): array + { + if (isset($options['username']) || isset($options['password'])) { + return $options; + } + + $username = getenv('MONGODB_USERNAME') ?: null; + $password = getenv('MONGODB_PASSWORD') ?: null; + + if ($username !== null) { + $options['username'] = $username; + } + + if ($password !== null) { + $options['password'] = $password; + } + + return $options; + } + + private static function appendServerApiOption(array $driverOptions): array + { + if (getenv('API_VERSION') && ! isset($driverOptions['serverApi'])) { + $driverOptions['serverApi'] = new ServerApi(getenv('API_VERSION')); + } + + return $driverOptions; + } + /** * Disables any fail points that were configured earlier in the test. * * This tracks fail points set via configureFailPoint() and should be called * during tearDown(). */ - private function disableFailPoints() + private function disableFailPoints(): void { if (empty($this->configuredFailPoints)) { return; } - foreach ($this->configuredFailPoints as list($failPoint, $server)) { + foreach ($this->configuredFailPoints as [$failPoint, $server]) { $operation = new DatabaseCommand('admin', ['configureFailPoint' => $failPoint, 'mode' => 'off']); $operation->execute($server); } } - /** - * @param string $row - * - * @return string|null - */ - private function getModuleInfo($row) + private static function getUriWithoutMultipleMongoses(): string { - ob_start(); - phpinfo(INFO_MODULES); - $info = ob_get_clean(); + /* Cache the result. We can safely assume the topology type will remain + * constant for the duration of the test suite. */ + static $uri; - $pattern = sprintf('/^%s([\w ]+)$/m', preg_quote($row . ' => ')); + if (isset($uri)) { + return $uri; + } - if (preg_match($pattern, $info, $matches) !== 1) { - return null; + $uri = parent::getUri(); + $parsed = parse_url($uri); + + if (! isset($parsed['scheme'], $parsed['host'])) { + throw new UnexpectedValueException('Failed to parse scheme and host components from URI: ' . $uri); } - return $matches[1]; + // Only modify URIs using the mongodb scheme + if ($parsed['scheme'] !== 'mongodb') { + return $uri; + } + + $hosts = explode(',', $parsed['host']); + $numHosts = count($hosts); + + if ($numHosts === 1) { + return $uri; + } + + $manager = static::createTestManager($uri); + if ($manager->selectServer()->getType() !== Server::TYPE_MONGOS) { + return $uri; + } + + // Re-append port to last host + if (isset($parsed['port'])) { + $hosts[$numHosts - 1] .= ':' . $parsed['port']; + } + + $parts = ['mongodb://']; + + if (isset($parsed['user'], $parsed['pass'])) { + $parts[] = $parsed['user'] . ':' . $parsed['pass'] . '@'; + } + + $parts[] = $hosts[0]; + + if (isset($parsed['path'])) { + $parts[] = $parsed['path']; + } elseif (isset($parsed['query'])) { + /* URIs containing connection options but no auth database component + * still require a slash before the question mark */ + $parts[] = '/'; + } + + if (isset($parsed['query'])) { + $parts[] = '?' . $parsed['query']; + } + + $uri = implode('', $parts); + + return $uri; } /** * Checks if the failCommand command is supported on this server version - * - * @return bool */ - private function isFailCommandSupported() + private function isFailCommandSupported(): bool { $minVersion = $this->isShardedCluster() ? '4.1.5' : '4.0.0'; @@ -472,10 +701,8 @@ private function isFailCommandSupported() /** * Checks if the failCommand command is enabled by checking the enableTestCommands parameter - * - * @return bool */ - private function isFailCommandEnabled() + private function isFailCommandEnabled(): bool { try { $cursor = $this->manager->executeCommand( diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index 6efba64b1..86fe31deb 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -2,25 +2,29 @@ namespace MongoDB\Tests; +use MongoDB\BSON\Document; +use MongoDB\BSON\PackedArray; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; + use function MongoDB\apply_type_map_to_document; use function MongoDB\create_field_path_type_map; -use function MongoDB\generate_index_name; +use function MongoDB\document_to_array; use function MongoDB\is_first_key_operator; +use function MongoDB\is_last_pipeline_operator_write; use function MongoDB\is_mapreduce_output_inline; use function MongoDB\is_pipeline; +use function MongoDB\is_write_concern_acknowledged; /** * Unit tests for utility functions. */ class FunctionsTest extends TestCase { - /** - * @dataProvider provideDocumentAndTypeMap - */ - public function testApplyTypeMapToDocument($document, array $typeMap, $expectedDocument) + /** @dataProvider provideDocumentAndTypeMap */ + public function testApplyTypeMapToDocument($document, array $typeMap, $expectedDocument): void { $this->assertEquals($expectedDocument, apply_type_map_to_document($document, $typeMap)); } @@ -89,85 +93,81 @@ public function provideDocumentAndTypeMap() ]; } - /** - * @dataProvider provideIndexSpecificationDocumentsAndGeneratedNames - */ - public function testGenerateIndexName($document, $expectedName) + /** @dataProvider provideDocumentsAndExpectedArrays */ + public function testDocumentToArray($document, array $expectedArray): void { - $this->assertSame($expectedName, generate_index_name($document)); + $this->assertSame($expectedArray, document_to_array($document)); } - public function provideIndexSpecificationDocumentsAndGeneratedNames() + public function provideDocumentsAndExpectedArrays(): array { return [ - [ ['x' => 1], 'x_1' ], - [ ['x' => -1, 'y' => 1], 'x_-1_y_1' ], - [ ['x' => '2dsphere', 'y' => 1 ], 'x_2dsphere_y_1' ], - [ (object) ['x' => 1], 'x_1' ], - [ new BSONDocument(['x' => 1]), 'x_1' ], + 'array' => [['x' => 1], ['x' => 1]], + 'object' => [(object) ['x' => 1], ['x' => 1]], + 'Serializable' => [new BSONDocument(['x' => 1]), ['x' => 1]], + 'Document' => [Document::fromPHP(['x' => 1]), ['x' => 1]], + // PackedArray and array-returning Serializable are both allowed + 'PackedArray' => [PackedArray::fromPHP(['foo']), [0 => 'foo']], + 'Serializable:array' => [new BSONArray(['foo']), [0 => 'foo']], ]; } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testGenerateIndexNameArgumentTypeCheck($document) + /** @dataProvider provideInvalidDocumentValues */ + public function testDocumentToArrayArgumentTypeCheck($document): void { $this->expectException(InvalidArgumentException::class); - generate_index_name($document); + $this->expectExceptionMessage('Expected $document to have type "array or object"'); + document_to_array($document); } - /** - * @dataProvider provideIsFirstKeyOperatorDocuments - */ - public function testIsFirstKeyOperator($document, $isFirstKeyOperator) + public function provideDocumentCasts(): array { - $this->assertSame($isFirstKeyOperator, is_first_key_operator($document)); + // phpcs:disable SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing + // phpcs:disable Squiz.Functions.MultiLineFunctionDeclaration + // phpcs:disable Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore + return [ + 'array' => [function ($value) { return $value; }], + 'object' => [function ($value) { return (object) $value; }], + 'Serializable' => [function ($value) { return new BSONDocument($value); }], + 'Document' => [function ($value) { return Document::fromPHP($value); }], + ]; + // phpcs:enable } - public function provideIsFirstKeyOperatorDocuments() + /** @dataProvider provideDocumentCasts */ + public function testIsFirstKeyOperator(callable $cast): void { - return [ - [ ['y' => 1], false ], - [ (object) ['y' => 1], false ], - [ new BSONDocument(['y' => 1]), false ], - [ ['$set' => ['y' => 1]], true ], - [ (object) ['$set' => ['y' => 1]], true ], - [ new BSONDocument(['$set' => ['y' => 1]]), true ], - ]; + $this->assertFalse(is_first_key_operator($cast(['y' => 1]))); + $this->assertTrue(is_first_key_operator($cast(['$set' => ['y' => 1]]))); + + // Empty and packed arrays are unlikely arguments, but still valid + $this->assertFalse(is_first_key_operator($cast([]))); + $this->assertFalse(is_first_key_operator($cast(['foo']))); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testIsFirstKeyOperatorArgumentTypeCheck($document) + /** @dataProvider provideInvalidDocumentValues */ + public function testIsFirstKeyOperatorArgumentTypeCheck($document): void { $this->expectException(InvalidArgumentException::class); is_first_key_operator($document); } - /** - * @dataProvider provideMapReduceOutValues - */ - public function testIsMapReduceOutputInline($out, $isInline) + /** @dataProvider provideDocumentCasts */ + public function testIsMapReduceOutputInlineWithDocumentValues(callable $cast): void { - $this->assertSame($isInline, is_mapreduce_output_inline($out)); + $this->assertTrue(is_mapreduce_output_inline($cast(['inline' => 1]))); + // Note: only the key is significant + $this->assertTrue(is_mapreduce_output_inline($cast(['inline' => 0]))); + $this->assertFalse(is_mapreduce_output_inline($cast(['replace' => 'collectionName']))); } - public function provideMapReduceOutValues() + public function testIsMapReduceOutputInlineWithStringValue(): void { - return [ - [ 'collectionName', false ], - [ ['inline' => 1], true ], - [ ['inline' => 0], true ], // only the key is significant - [ ['replace' => 'collectionName'], false ], - ]; + $this->assertFalse(is_mapreduce_output_inline('collectionName')); } - /** - * @dataProvider provideTypeMapValues - */ - public function testCreateFieldPathTypeMap(array $expected, array $typeMap, $fieldPath = 'field') + /** @dataProvider provideTypeMapValues */ + public function testCreateFieldPathTypeMap(array $expected, array $typeMap, $fieldPath = 'field'): void { $this->assertEquals($expected, create_field_path_type_map($typeMap, $fieldPath)); } @@ -194,7 +194,7 @@ public function provideTypeMapValues() 'Array field path converted to array' => [ [ 'root' => 'object', - 'array' => 'MongoDB\Model\BSONArray', + 'array' => BSONArray::class, 'fieldPaths' => [ 'field' => 'array', 'field.$' => 'object', @@ -203,7 +203,7 @@ public function provideTypeMapValues() ], [ 'root' => 'object', - 'array' => 'MongoDB\Model\BSONArray', + 'array' => BSONArray::class, 'fieldPaths' => ['nested' => 'array'], ], 'field.$', @@ -211,14 +211,14 @@ public function provideTypeMapValues() 'Array field path without root key' => [ [ 'root' => 'object', - 'array' => 'MongoDB\Model\BSONArray', + 'array' => BSONArray::class, 'fieldPaths' => [ 'field' => 'array', 'field.$.nested' => 'array', ], ], [ - 'array' => 'MongoDB\Model\BSONArray', + 'array' => BSONArray::class, 'fieldPaths' => ['nested' => 'array'], ], 'field.$', @@ -226,32 +226,108 @@ public function provideTypeMapValues() ]; } - /** - * @dataProvider providePipelines - */ - public function testIsPipeline($expected, $pipeline) + /** @dataProvider provideDocumentCasts */ + public function testIsLastPipelineOperatorWrite(callable $cast): void + { + $match = ['$match' => ['x' => 1]]; + $merge = ['$merge' => ['into' => 'coll']]; + $out = ['$out' => ['db' => 'db', 'coll' => 'coll']]; + + $this->assertTrue(is_last_pipeline_operator_write([$cast($merge)])); + $this->assertTrue(is_last_pipeline_operator_write([$cast($out)])); + $this->assertTrue(is_last_pipeline_operator_write([$cast($match), $cast($merge)])); + $this->assertTrue(is_last_pipeline_operator_write([$cast($match), $cast($out)])); + $this->assertFalse(is_last_pipeline_operator_write([$cast($match)])); + $this->assertFalse(is_last_pipeline_operator_write([$cast($merge), $cast($match)])); + $this->assertFalse(is_last_pipeline_operator_write([$cast($out), $cast($match)])); + } + + /** @dataProvider providePipelines */ + public function testIsPipeline($expected, $pipeline, $allowEmpty = false): void + { + $this->assertSame($expected, is_pipeline($pipeline, $allowEmpty)); + } + + public function providePipelines(): array + { + $valid = [ + ['$match' => ['foo' => 'bar']], + (object) ['$group' => ['_id' => 1]], + new BSONDocument(['$skip' => 1]), + Document::fromPHP(['$limit' => 1]), + ]; + + $invalidIndex = [1 => ['$group' => ['_id' => 1]]]; + + $invalidStageKey = [['group' => ['_id' => 1]]]; + + $dbrefInNumericField = ['0' => ['$ref' => 'foo', '$id' => 'bar']]; + + return [ + // Valid pipeline in various forms + 'valid: array' => [true, $valid], + 'valid: Serializable' => [true, new BSONArray($valid)], + 'valid: PackedArray' => [true, PackedArray::fromPHP($valid)], + // Invalid type for an otherwise valid pipeline + 'invalid type: stdClass' => [false, (object) $valid], + 'invalid type: Serializable' => [false, new BSONDocument($valid)], + 'invalid type: Document' => [false, Document::fromPHP($valid)], + // Invalid index in pipeline array + 'invalid index: array' => [false, $invalidIndex], + // Note: PackedArray::fromPHP() requires a list array + // Note: BSONArray::bsonSerialize() re-indexes the array + 'invalid index: array' => [true, new BSONArray($invalidIndex)], + // Invalid stage key in pipeline element + 'invalid stage key: array' => [false, $invalidStageKey], + 'invalid stage key: Serializable' => [false, new BSONArray($invalidStageKey)], + 'invalid stage key: PackedArray' => [false, PackedArray::fromPHP($invalidStageKey)], + // Invalid pipeline element type + 'invalid pipeline element type: array' => [false, [[[]]]], + 'invalid pipeline element type: Serializable' => [false, new BSONArray([new BSONArray([])])], + 'invalid pipeline element type: PackedArray' => [false, PackedArray::fromPHP([[]])], + // Empty array has no pipeline stages + 'valid empty: array' => [true, [], true], + 'valid empty: Serializable' => [true, new BSONArray([]), true], + 'valid empty: PackedArray' => [true, PackedArray::fromPHP([]), true], + 'invalid empty: array' => [false, []], + 'invalid empty: Serializable' => [false, new BSONArray([])], + 'invalid empty: PackedArray' => [false, PackedArray::fromPHP([])], + // False positive: DBRef in numeric field + 'false positive DBRef: array' => [true, $dbrefInNumericField], + 'false positive DBRef: Serializable' => [true, new BSONArray($dbrefInNumericField)], + 'false positive DBRef: PackedArray' => [true, PackedArray::fromPHP($dbrefInNumericField)], + // Invalid document containing DBRef in numeric field + 'invalid DBRef: stdClass' => [false, (object) $dbrefInNumericField], + 'invalid DBRef: Serializable' => [false, new BSONDocument($dbrefInNumericField)], + 'invalid DBRef: Document' => [false, Document::fromPHP($dbrefInNumericField)], + // Additional invalid cases + 'Update document' => [false, ['$set' => ['foo' => 'bar']]], + 'Replacement document with DBRef' => [false, ['x' => ['$ref' => 'foo', '$id' => 'bar']]], + ]; + } + + /** @dataProvider provideWriteConcerns */ + public function testIsWriteConcernAcknowledged($expected, WriteConcern $writeConcern): void { - $this->assertSame($expected, is_pipeline($pipeline)); + $this->assertSame($expected, is_write_concern_acknowledged($writeConcern)); } - public function providePipelines() + public function provideWriteConcerns(): array { + // Note: WriteConcern constructor prohibits w=-1 or w=0 and journal=true return [ - 'Not an array' => [false, (object) []], - 'Empty array' => [false, []], - 'Non-sequential indexes in array' => [false, [1 => ['$group' => []]]], - 'Update document instead of pipeline' => [false, ['$set' => ['foo' => 'bar']]], - 'Invalid pipeline stage' => [false, [['group' => []]]], - 'Update with DbRef' => [false, ['x' => ['$ref' => 'foo', '$id' => 'bar']]], - 'Valid pipeline' => [ - true, - [ - ['$match' => ['foo' => 'bar']], - ['$group' => ['_id' => 1]], - ], - ], - 'False positive with DbRef in numeric field' => [true, ['0' => ['$ref' => 'foo', '$id' => 'bar']]], - 'DbRef in numeric field as object' => [false, (object) ['0' => ['$ref' => 'foo', '$id' => 'bar']]], + 'MONGOC_WRITE_CONCERN_W_MAJORITY' => [true, new WriteConcern(-3)], + 'MONGOC_WRITE_CONCERN_W_DEFAULT' => [true, new WriteConcern(-2)], + 'MONGOC_WRITE_CONCERN_W_DEFAULT and journal=true' => [true, new WriteConcern(-2, 0, true)], + 'MONGOC_WRITE_CONCERN_W_ERRORS_IGNORED' => [false, new WriteConcern(-1)], + 'MONGOC_WRITE_CONCERN_W_ERRORS_IGNORED and journal=false' => [false, new WriteConcern(-1, 0, false)], + 'MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED' => [false, new WriteConcern(0)], + 'MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED and journal=false' => [false, new WriteConcern(0, 0, false)], + 'w=1' => [true, new WriteConcern(1)], + 'w=1 and journal=false' => [true, new WriteConcern(1, 0, false)], + 'w=1 and journal=true' => [true, new WriteConcern(1, 0, true)], + 'majority' => [true, new WriteConcern(WriteConcern::MAJORITY)], + 'tag' => [true, new WriteConcern('tag')], ]; } } diff --git a/tests/GridFS/BucketFunctionalTest.php b/tests/GridFS/BucketFunctionalTest.php index 069b79b91..27a22ff90 100644 --- a/tests/GridFS/BucketFunctionalTest.php +++ b/tests/GridFS/BucketFunctionalTest.php @@ -9,13 +9,13 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\GridFS\Bucket; +use MongoDB\GridFS\Exception\CorruptFileException; use MongoDB\GridFS\Exception\FileNotFoundException; use MongoDB\GridFS\Exception\StreamException; use MongoDB\Model\BSONDocument; use MongoDB\Model\IndexInfo; -use MongoDB\Operation\ListCollections; use MongoDB\Operation\ListIndexes; -use PHPUnit\Framework\Error\Warning; + use function array_merge; use function call_user_func; use function current; @@ -34,6 +34,7 @@ use function strlen; use function strncasecmp; use function substr; + use const PHP_EOL; use const PHP_OS; use const PHP_VERSION_ID; @@ -43,24 +44,21 @@ */ class BucketFunctionalTest extends FunctionalTestCase { - /** - * @doesNotPerformAssertions - */ - public function testValidConstructorOptions() + /** @doesNotPerformAssertions */ + public function testValidConstructorOptions(): void { new Bucket($this->manager, $this->getDatabaseName(), [ 'bucketName' => 'test', 'chunkSizeBytes' => 8192, 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY, 1000), + 'disableMD5' => true, ]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Bucket($this->manager, $this->getDatabaseName(), $options); @@ -101,17 +99,15 @@ public function provideInvalidConstructorOptions() return $options; } - public function testConstructorShouldRequireChunkSizeBytesOptionToBePositive() + public function testConstructorShouldRequireChunkSizeBytesOptionToBePositive(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Expected "chunkSizeBytes" option to be >= 1, 0 given'); new Bucket($this->manager, $this->getDatabaseName(), ['chunkSizeBytes' => 0]); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ - public function testDelete($input, $expectedChunks) + /** @dataProvider provideInputDataAndExpectedChunks */ + public function testDelete($input, $expectedChunks): void { $id = $this->bucket->uploadFromStream('filename', $this->createStream($input)); @@ -140,16 +136,14 @@ public function provideInputDataAndExpectedChunks() ]; } - public function testDeleteShouldRequireFileToExist() + public function testDeleteShouldRequireFileToExist(): void { $this->expectException(FileNotFoundException::class); $this->bucket->delete('nonexistent-id'); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ - public function testDeleteStillRemovesChunksIfFileDoesNotExist($input, $expectedChunks) + /** @dataProvider provideInputDataAndExpectedChunks */ + public function testDeleteStillRemovesChunksIfFileDoesNotExist($input, $expectedChunks): void { $id = $this->bucket->uploadFromStream('filename', $this->createStream($input)); @@ -167,17 +161,18 @@ public function testDeleteStillRemovesChunksIfFileDoesNotExist($input, $expected $this->assertCollectionCount($this->chunksCollection, 0); } - public function testDownloadingFileWithMissingChunk() + public function testDownloadingFileWithMissingChunk(): void { $id = $this->bucket->uploadFromStream("filename", $this->createStream("foobar")); $this->chunksCollection->deleteOne(['files_id' => $id, 'n' => 0]); - $this->expectException(Warning::class); + $this->expectException(CorruptFileException::class); + $this->expectExceptionMessage('Chunk not found for index "0"'); stream_get_contents($this->bucket->openDownloadStream($id)); } - public function testDownloadingFileWithUnexpectedChunkIndex() + public function testDownloadingFileWithUnexpectedChunkIndex(): void { $id = $this->bucket->uploadFromStream("filename", $this->createStream("foobar")); @@ -186,11 +181,12 @@ public function testDownloadingFileWithUnexpectedChunkIndex() ['$set' => ['n' => 1]] ); - $this->expectException(Warning::class); + $this->expectException(CorruptFileException::class); + $this->expectExceptionMessage('Expected chunk to have index "0" but found "1"'); stream_get_contents($this->bucket->openDownloadStream($id)); } - public function testDownloadingFileWithUnexpectedChunkSize() + public function testDownloadingFileWithUnexpectedChunkSize(): void { $id = $this->bucket->uploadFromStream("filename", $this->createStream("foobar")); @@ -199,14 +195,13 @@ public function testDownloadingFileWithUnexpectedChunkSize() ['$set' => ['data' => new Binary('fooba', Binary::TYPE_GENERIC)]] ); - $this->expectException(Warning::class); + $this->expectException(CorruptFileException::class); + $this->expectExceptionMessage('Expected chunk to have size "6" but found "5"'); stream_get_contents($this->bucket->openDownloadStream($id)); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ - public function testDownloadToStream($input) + /** @dataProvider provideInputDataAndExpectedChunks */ + public function testDownloadToStream($input): void { $id = $this->bucket->uploadFromStream('filename', $this->createStream($input)); $destination = $this->createStream(); @@ -215,10 +210,8 @@ public function testDownloadToStream($input) $this->assertStreamContents($input, $destination); } - /** - * @dataProvider provideInvalidStreamValues - */ - public function testDownloadToStreamShouldRequireDestinationStream($destination) + /** @dataProvider provideInvalidStreamValues */ + public function testDownloadToStreamShouldRequireDestinationStream($destination): void { $this->expectException(InvalidArgumentException::class); $this->bucket->downloadToStream('id', $destination); @@ -229,13 +222,13 @@ public function provideInvalidStreamValues() return $this->wrapValuesForDataProvider($this->getInvalidStreamValues()); } - public function testDownloadToStreamShouldRequireFileToExist() + public function testDownloadToStreamShouldRequireFileToExist(): void { $this->expectException(FileNotFoundException::class); $this->bucket->downloadToStream('nonexistent-id', $this->createStream()); } - public function testDownloadToStreamByName() + public function testDownloadToStreamByName(): void { $this->bucket->uploadFromStream('filename', $this->createStream('foo')); $this->bucket->uploadFromStream('filename', $this->createStream('bar')); @@ -270,19 +263,15 @@ public function testDownloadToStreamByName() $this->assertStreamContents('baz', $destination); } - /** - * @dataProvider provideInvalidStreamValues - */ - public function testDownloadToStreamByNameShouldRequireDestinationStream($destination) + /** @dataProvider provideInvalidStreamValues */ + public function testDownloadToStreamByNameShouldRequireDestinationStream($destination): void { $this->expectException(InvalidArgumentException::class); $this->bucket->downloadToStreamByName('filename', $destination); } - /** - * @dataProvider provideNonexistentFilenameAndRevision - */ - public function testDownloadToStreamByNameShouldRequireFilenameAndRevisionToExist($filename, $revision) + /** @dataProvider provideNonexistentFilenameAndRevision */ + public function testDownloadToStreamByNameShouldRequireFilenameAndRevisionToExist($filename, $revision): void { $this->bucket->uploadFromStream('filename', $this->createStream('foo')); $this->bucket->uploadFromStream('filename', $this->createStream('bar')); @@ -302,7 +291,7 @@ public function provideNonexistentFilenameAndRevision() ]; } - public function testDrop() + public function testDrop(): void { $this->bucket->uploadFromStream('filename', $this->createStream('foobar')); @@ -315,7 +304,7 @@ public function testDrop() $this->assertCollectionDoesNotExist($this->chunksCollection->getCollectionName()); } - public function testFind() + public function testFind(): void { $this->bucket->uploadFromStream('a', $this->createStream('foo')); $this->bucket->uploadFromStream('b', $this->createStream('foobar')); @@ -341,7 +330,7 @@ public function testFind() $this->assertSameDocuments($expected, $cursor); } - public function testFindUsesTypeMap() + public function testFindUsesTypeMap(): void { $this->bucket->uploadFromStream('a', $this->createStream('foo')); @@ -351,7 +340,7 @@ public function testFindUsesTypeMap() $this->assertInstanceOf(BSONDocument::class, $fileDocument); } - public function testFindOne() + public function testFindOne(): void { $this->bucket->uploadFromStream('a', $this->createStream('foo')); $this->bucket->uploadFromStream('b', $this->createStream('foobar')); @@ -373,19 +362,19 @@ public function testFindOne() $this->assertSameDocument(['filename' => 'b', 'length' => 6], $fileDocument); } - public function testGetBucketNameWithCustomValue() + public function testGetBucketNameWithCustomValue(): void { $bucket = new Bucket($this->manager, $this->getDatabaseName(), ['bucketName' => 'custom_fs']); $this->assertEquals('custom_fs', $bucket->getBucketName()); } - public function testGetBucketNameWithDefaultValue() + public function testGetBucketNameWithDefaultValue(): void { $this->assertEquals('fs', $this->bucket->getBucketName()); } - public function testGetChunksCollection() + public function testGetChunksCollection(): void { $chunksCollection = $this->bucket->getChunksCollection(); @@ -393,24 +382,24 @@ public function testGetChunksCollection() $this->assertEquals('fs.chunks', $chunksCollection->getCollectionName()); } - public function testGetChunkSizeBytesWithCustomValue() + public function testGetChunkSizeBytesWithCustomValue(): void { $bucket = new Bucket($this->manager, $this->getDatabaseName(), ['chunkSizeBytes' => 8192]); $this->assertEquals(8192, $bucket->getChunkSizeBytes()); } - public function testGetChunkSizeBytesWithDefaultValue() + public function testGetChunkSizeBytesWithDefaultValue(): void { $this->assertEquals(261120, $this->bucket->getChunkSizeBytes()); } - public function testGetDatabaseName() + public function testGetDatabaseName(): void { $this->assertEquals($this->getDatabaseName(), $this->bucket->getDatabaseName()); } - public function testGetFileDocumentForStreamUsesTypeMap() + public function testGetFileDocumentForStreamUsesTypeMap(): void { $metadata = ['foo' => 'bar']; $stream = $this->bucket->openUploadStream('filename', ['_id' => 1, 'metadata' => $metadata]); @@ -422,7 +411,7 @@ public function testGetFileDocumentForStreamUsesTypeMap() $this->assertSame(['foo' => 'bar'], $fileDocument['metadata']->getArrayCopy()); } - public function testGetFileDocumentForStreamWithReadableStream() + public function testGetFileDocumentForStreamWithReadableStream(): void { $metadata = ['foo' => 'bar']; $id = $this->bucket->uploadFromStream('filename', $this->createStream('foobar'), ['metadata' => $metadata]); @@ -436,7 +425,7 @@ public function testGetFileDocumentForStreamWithReadableStream() $this->assertSameDocument($metadata, $fileDocument->metadata); } - public function testGetFileDocumentForStreamWithWritableStream() + public function testGetFileDocumentForStreamWithWritableStream(): void { $metadata = ['foo' => 'bar']; $stream = $this->bucket->openUploadStream('filename', ['_id' => 1, 'metadata' => $metadata]); @@ -448,10 +437,8 @@ public function testGetFileDocumentForStreamWithWritableStream() $this->assertSameDocument($metadata, $fileDocument->metadata); } - /** - * @dataProvider provideInvalidGridFSStreamValues - */ - public function testGetFileDocumentForStreamShouldRequireGridFSStreamResource($stream) + /** @dataProvider provideInvalidGridFSStreamValues */ + public function testGetFileDocumentForStreamShouldRequireGridFSStreamResource($stream): void { $this->expectException(InvalidArgumentException::class); $this->bucket->getFileDocumentForStream($stream); @@ -462,7 +449,7 @@ public function provideInvalidGridFSStreamValues() return $this->wrapValuesForDataProvider(array_merge($this->getInvalidStreamValues(), [$this->createStream()])); } - public function testGetFileIdForStreamUsesTypeMap() + public function testGetFileIdForStreamUsesTypeMap(): void { $stream = $this->bucket->openUploadStream('filename', ['_id' => ['x' => 1]]); @@ -472,7 +459,7 @@ public function testGetFileIdForStreamUsesTypeMap() $this->assertSame(['x' => 1], $id->getArrayCopy()); } - public function testGetFileIdForStreamWithReadableStream() + public function testGetFileIdForStreamWithReadableStream(): void { $id = $this->bucket->uploadFromStream('filename', $this->createStream('foobar')); $stream = $this->bucket->openDownloadStream($id); @@ -480,23 +467,21 @@ public function testGetFileIdForStreamWithReadableStream() $this->assertSameObjectId($id, $this->bucket->getFileIdForStream($stream)); } - public function testGetFileIdForStreamWithWritableStream() + public function testGetFileIdForStreamWithWritableStream(): void { $stream = $this->bucket->openUploadStream('filename', ['_id' => 1]); $this->assertEquals(1, $this->bucket->getFileIdForStream($stream)); } - /** - * @dataProvider provideInvalidGridFSStreamValues - */ - public function testGetFileIdForStreamShouldRequireGridFSStreamResource($stream) + /** @dataProvider provideInvalidGridFSStreamValues */ + public function testGetFileIdForStreamShouldRequireGridFSStreamResource($stream): void { $this->expectException(InvalidArgumentException::class); $this->bucket->getFileIdForStream($stream); } - public function testGetFilesCollection() + public function testGetFilesCollection(): void { $filesCollection = $this->bucket->getFilesCollection(); @@ -504,20 +489,16 @@ public function testGetFilesCollection() $this->assertEquals('fs.files', $filesCollection->getCollectionName()); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ - public function testOpenDownloadStream($input) + /** @dataProvider provideInputDataAndExpectedChunks */ + public function testOpenDownloadStream($input): void { $id = $this->bucket->uploadFromStream('filename', $this->createStream($input)); $this->assertStreamContents($input, $this->bucket->openDownloadStream($id)); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ - public function testOpenDownloadStreamAndMultipleReadOperations($input) + /** @dataProvider provideInputDataAndExpectedChunks */ + public function testOpenDownloadStreamAndMultipleReadOperations($input): void { $id = $this->bucket->uploadFromStream('filename', $this->createStream($input)); $stream = $this->bucket->openDownloadStream($id); @@ -535,19 +516,19 @@ public function testOpenDownloadStreamAndMultipleReadOperations($input) $this->assertEquals($input, $buffer); } - public function testOpenDownloadStreamShouldRequireFileToExist() + public function testOpenDownloadStreamShouldRequireFileToExist(): void { $this->expectException(FileNotFoundException::class); $this->bucket->openDownloadStream('nonexistent-id'); } - public function testOpenDownloadStreamByNameShouldRequireFilenameToExist() + public function testOpenDownloadStreamByNameShouldRequireFilenameToExist(): void { $this->expectException(FileNotFoundException::class); $this->bucket->openDownloadStream('nonexistent-filename'); } - public function testOpenDownloadStreamByName() + public function testOpenDownloadStreamByName(): void { $this->bucket->uploadFromStream('filename', $this->createStream('foo')); $this->bucket->uploadFromStream('filename', $this->createStream('bar')); @@ -562,19 +543,17 @@ public function testOpenDownloadStreamByName() $this->assertStreamContents('baz', $this->bucket->openDownloadStreamByName('filename', ['revision' => 2])); } - /** - * @dataProvider provideNonexistentFilenameAndRevision - */ - public function testOpenDownloadStreamByNameShouldRequireFilenameAndRevisionToExist($filename, $revision) + /** @dataProvider provideNonexistentFilenameAndRevision */ + public function testOpenDownloadStreamByNameShouldRequireFilenameAndRevisionToExist($filename, $revision): void { $this->bucket->uploadFromStream('filename', $this->createStream('foo')); $this->bucket->uploadFromStream('filename', $this->createStream('bar')); $this->expectException(FileNotFoundException::class); - $this->bucket->openDownloadStream($filename, ['revision' => $revision]); + $this->bucket->openDownloadStreamByName($filename, ['revision' => $revision]); } - public function testOpenUploadStream() + public function testOpenUploadStream(): void { $stream = $this->bucket->openUploadStream('filename'); @@ -584,10 +563,8 @@ public function testOpenUploadStream() $this->assertStreamContents('foobar', $this->bucket->openDownloadStreamByName('filename')); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ - public function testOpenUploadStreamAndMultipleWriteOperations($input) + /** @dataProvider provideInputDataAndExpectedChunks */ + public function testOpenUploadStreamAndMultipleWriteOperations($input): void { $stream = $this->bucket->openUploadStream('filename'); $offset = 0; @@ -604,7 +581,7 @@ public function testOpenUploadStreamAndMultipleWriteOperations($input) $this->assertStreamContents($input, $this->bucket->openDownloadStreamByName('filename')); } - public function testRename() + public function testRename(): void { $id = $this->bucket->uploadFromStream('a', $this->createStream('foo')); $this->bucket->rename($id, 'b'); @@ -618,7 +595,7 @@ public function testRename() $this->assertStreamContents('foo', $this->bucket->openDownloadStreamByName('b')); } - public function testRenameShouldNotRequireFileToBeModified() + public function testRenameShouldNotRequireFileToBeModified(): void { $id = $this->bucket->uploadFromStream('a', $this->createStream('foo')); $this->bucket->rename($id, 'a'); @@ -632,13 +609,13 @@ public function testRenameShouldNotRequireFileToBeModified() $this->assertStreamContents('foo', $this->bucket->openDownloadStreamByName('a')); } - public function testRenameShouldRequireFileToExist() + public function testRenameShouldRequireFileToExist(): void { $this->expectException(FileNotFoundException::class); $this->bucket->rename('nonexistent-id', 'b'); } - public function testUploadFromStream() + public function testUploadFromStream(): void { $options = [ '_id' => 'custom-id', @@ -657,16 +634,14 @@ public function testUploadFromStream() $this->assertSameDocument(['foo' => 'bar'], $fileDocument['metadata']); } - /** - * @dataProvider provideInvalidStreamValues - */ - public function testUploadFromStreamShouldRequireSourceStream($source) + /** @dataProvider provideInvalidStreamValues */ + public function testUploadFromStreamShouldRequireSourceStream($source): void { $this->expectException(InvalidArgumentException::class); $this->bucket->uploadFromStream('filename', $source); } - public function testUploadingAnEmptyFile() + public function testUploadingAnEmptyFile(): void { $id = $this->bucket->uploadFromStream('filename', $this->createStream('')); $destination = $this->createStream(); @@ -695,17 +670,43 @@ public function testUploadingAnEmptyFile() $this->assertSameDocument($expected, $fileDocument); } - public function testUploadingFirstFileCreatesIndexes() + public function testDisableMD5(): void + { + $options = ['disableMD5' => true]; + $id = $this->bucket->uploadFromStream('filename', $this->createStream('data'), $options); + + $fileDocument = $this->filesCollection->findOne( + ['_id' => $id] + ); + + $this->assertArrayNotHasKey('md5', $fileDocument); + } + + public function testDisableMD5OptionInConstructor(): void + { + $options = ['disableMD5' => true]; + + $this->bucket = new Bucket($this->manager, $this->getDatabaseName(), $options); + $id = $this->bucket->uploadFromStream('filename', $this->createStream('data')); + + $fileDocument = $this->filesCollection->findOne( + ['_id' => $id] + ); + + $this->assertArrayNotHasKey('md5', $fileDocument); + } + + public function testUploadingFirstFileCreatesIndexes(): void { $this->bucket->uploadFromStream('filename', $this->createStream('foo')); $this->assertIndexExists($this->filesCollection->getCollectionName(), 'filename_1_uploadDate_1'); - $this->assertIndexExists($this->chunksCollection->getCollectionName(), 'files_id_1_n_1', function (IndexInfo $info) { + $this->assertIndexExists($this->chunksCollection->getCollectionName(), 'files_id_1_n_1', function (IndexInfo $info): void { $this->assertTrue($info->isUnique()); }); } - public function testExistingIndexIsReused() + public function testExistingIndexIsReused(): void { $this->filesCollection->createIndex(['filename' => 1.0, 'uploadDate' => 1], ['name' => 'test']); $this->chunksCollection->createIndex(['files_id' => 1.0, 'n' => 1], ['name' => 'test', 'unique' => true]); @@ -716,7 +717,7 @@ public function testExistingIndexIsReused() $this->assertIndexNotExists($this->chunksCollection->getCollectionName(), 'files_id_1_n_1'); } - public function testDownloadToStreamFails() + public function testDownloadToStreamFails(): void { $this->bucket->uploadFromStream('filename', $this->createStream('foo'), ['_id' => ['foo' => 'bar']]); @@ -725,7 +726,7 @@ public function testDownloadToStreamFails() $this->bucket->downloadToStream(['foo' => 'bar'], fopen('php://temp', 'r')); } - public function testDownloadToStreamByNameFails() + public function testDownloadToStreamByNameFails(): void { $this->bucket->uploadFromStream('filename', $this->createStream('foo')); @@ -734,7 +735,7 @@ public function testDownloadToStreamByNameFails() $this->bucket->downloadToStreamByName('filename', fopen('php://temp', 'r')); } - public function testUploadFromStreamFails() + public function testUploadFromStreamFails(): void { if (PHP_VERSION_ID < 70400) { $this->markTestSkipped('Test only works on PHP 7.4 and newer'); @@ -748,7 +749,7 @@ public function testUploadFromStreamFails() $this->bucket->uploadFromStream('filename', $source); } - public function testDanglingOpenWritableStream() + public function testDanglingOpenWritableStream(): void { if (! strncasecmp(PHP_OS, 'WIN', 3)) { $this->markTestSkipped('Test does not apply to Windows'); @@ -771,29 +772,6 @@ public function testDanglingOpenWritableStream() $this->assertSame('', $output); } - /** - * Asserts that a collection with the given name does not exist on the - * server. - * - * @param string $collectionName - */ - private function assertCollectionDoesNotExist($collectionName) - { - $operation = new ListCollections($this->getDatabaseName()); - $collections = $operation->execute($this->getPrimaryServer()); - - $foundCollection = null; - - foreach ($collections as $collection) { - if ($collection->getName() === $collectionName) { - $foundCollection = $collection; - break; - } - } - - $this->assertNull($foundCollection, sprintf('Collection %s exists', $collectionName)); - } - /** * Asserts that an index with the given name exists for the collection. * @@ -801,12 +779,8 @@ private function assertCollectionDoesNotExist($collectionName) * argument as its first and only parameter. If an IndexInfo matching the * given name is found, it will be passed to the callback, which may perform * additional assertions. - * - * @param string $collectionName - * @param string $indexName - * @param callable $callback */ - private function assertIndexExists($collectionName, $indexName, $callback = null) + private function assertIndexExists(string $collectionName, string $indexName, ?callable $callback = null): void { if ($callback !== null && ! is_callable($callback)) { throw new InvalidArgumentException('$callback is not a callable'); @@ -833,11 +807,8 @@ private function assertIndexExists($collectionName, $indexName, $callback = null /** * Asserts that an index with the given name does not exist for the collection. - * - * @param string $collectionName - * @param string $indexName */ - private function assertIndexNotExists($collectionName, $indexName) + private function assertIndexNotExists(string $collectionName, string $indexName): void { $operation = new ListIndexes($this->getDatabaseName(), $collectionName); $indexes = $operation->execute($this->getPrimaryServer()); @@ -856,10 +827,8 @@ private function assertIndexNotExists($collectionName, $indexName) /** * Return a list of invalid stream values. - * - * @return array */ - private function getInvalidStreamValues() + private function getInvalidStreamValues(): array { return [null, 123, 'foo', [], hash_init('md5')]; } diff --git a/tests/GridFS/FunctionalTestCase.php b/tests/GridFS/FunctionalTestCase.php index 428195284..c22143f20 100644 --- a/tests/GridFS/FunctionalTestCase.php +++ b/tests/GridFS/FunctionalTestCase.php @@ -5,7 +5,7 @@ use MongoDB\Collection; use MongoDB\GridFS\Bucket; use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; + use function fopen; use function fwrite; use function get_resource_type; @@ -17,8 +17,6 @@ */ abstract class FunctionalTestCase extends BaseFunctionalTestCase { - use SetUpTearDownTrait; - /** @var Bucket */ protected $bucket; @@ -28,15 +26,15 @@ abstract class FunctionalTestCase extends BaseFunctionalTestCase /** @var Collection */ protected $filesCollection; - private function doSetUp() + public function setUp(): void { parent::setUp(); $this->bucket = new Bucket($this->manager, $this->getDatabaseName()); $this->bucket->drop(); - $this->chunksCollection = new Collection($this->manager, $this->getDatabaseName(), 'fs.chunks'); - $this->filesCollection = new Collection($this->manager, $this->getDatabaseName(), 'fs.files'); + $this->chunksCollection = $this->createCollection($this->getDatabaseName(), 'fs.chunks'); + $this->filesCollection = $this->createCollection($this->getDatabaseName(), 'fs.files'); } /** @@ -44,10 +42,9 @@ private function doSetUp() * * Note: this will seek to the beginning of the stream before reading. * - * @param string $expectedContents * @param resource $stream */ - protected function assertStreamContents($expectedContents, $stream) + protected function assertStreamContents(string $expectedContents, $stream): void { $this->assertIsResource($stream); $this->assertSame('stream', get_resource_type($stream)); @@ -57,10 +54,9 @@ protected function assertStreamContents($expectedContents, $stream) /** * Creates an in-memory stream with the given data. * - * @param string $data * @return resource */ - protected function createStream($data = '') + protected function createStream(string $data = '') { $stream = fopen('php://temp', 'w+b'); fwrite($stream, $data); diff --git a/tests/GridFS/ReadableStreamFunctionalTest.php b/tests/GridFS/ReadableStreamFunctionalTest.php index bdadcfa32..beabb7569 100644 --- a/tests/GridFS/ReadableStreamFunctionalTest.php +++ b/tests/GridFS/ReadableStreamFunctionalTest.php @@ -8,7 +8,7 @@ use MongoDB\GridFS\Exception\CorruptFileException; use MongoDB\GridFS\ReadableStream; use MongoDB\Tests\CommandObserver; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; + use function array_filter; /** @@ -16,12 +16,10 @@ */ class ReadableStreamFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; - /** @var CollectionWrapper */ private $collectionWrapper; - private function doSetUp() + public function setUp(): void { parent::setUp(); @@ -46,17 +44,15 @@ private function doSetUp() ]); } - public function testGetFile() + public function testGetFile(): void { $fileDocument = (object) ['_id' => null, 'chunkSize' => 1, 'length' => 0]; $stream = new ReadableStream($this->collectionWrapper, $fileDocument); $this->assertSame($fileDocument, $stream->getFile()); } - /** - * @dataProvider provideInvalidConstructorFileDocuments - */ - public function testConstructorFileDocumentChecks($file) + /** @dataProvider provideInvalidConstructorFileDocuments */ + public function testConstructorFileDocumentChecks($file): void { $this->expectException(CorruptFileException::class); new ReadableStream($this->collectionWrapper, $file); @@ -81,10 +77,8 @@ public function provideInvalidConstructorFileDocuments() return $options; } - /** - * @dataProvider provideFileIdAndExpectedBytes - */ - public function testReadBytes($fileId, $length, $expectedBytes) + /** @dataProvider provideFileIdAndExpectedBytes */ + public function testReadBytes($fileId, $length, $expectedBytes): void { $fileDocument = $this->collectionWrapper->findFileById($fileId); $stream = new ReadableStream($this->collectionWrapper, $fileDocument); @@ -128,10 +122,8 @@ function (array $args) { ); } - /** - * @dataProvider provideFilteredFileIdAndExpectedBytes - */ - public function testReadBytesCalledMultipleTimes($fileId, $length, $expectedBytes) + /** @dataProvider provideFilteredFileIdAndExpectedBytes */ + public function testReadBytesCalledMultipleTimes($fileId, $length, $expectedBytes): void { $fileDocument = $this->collectionWrapper->findFileById($fileId); $stream = new ReadableStream($this->collectionWrapper, $fileDocument); @@ -141,7 +133,7 @@ public function testReadBytesCalledMultipleTimes($fileId, $length, $expectedByte } } - public function testReadBytesWithMissingChunk() + public function testReadBytesWithMissingChunk(): void { $this->chunksCollection->deleteOne(['files_id' => 'length-10', 'n' => 2]); @@ -153,7 +145,7 @@ public function testReadBytesWithMissingChunk() $stream->readBytes(10); } - public function testReadBytesWithUnexpectedChunkIndex() + public function testReadBytesWithUnexpectedChunkIndex(): void { $this->chunksCollection->deleteOne(['files_id' => 'length-10', 'n' => 1]); @@ -165,7 +157,7 @@ public function testReadBytesWithUnexpectedChunkIndex() $stream->readBytes(10); } - public function testReadBytesWithUnexpectedChunkSize() + public function testReadBytesWithUnexpectedChunkSize(): void { $this->chunksCollection->updateOne( ['files_id' => 'length-10', 'n' => 2], @@ -180,7 +172,7 @@ public function testReadBytesWithUnexpectedChunkSize() $stream->readBytes(10); } - public function testReadBytesWithNegativeLength() + public function testReadBytesWithNegativeLength(): void { $fileDocument = $this->collectionWrapper->findFileById('length-0'); $stream = new ReadableStream($this->collectionWrapper, $fileDocument); @@ -189,7 +181,7 @@ public function testReadBytesWithNegativeLength() $stream->readBytes(-1); } - public function testSeekBeforeReading() + public function testSeekBeforeReading(): void { $fileDocument = $this->collectionWrapper->findFileById('length-10'); $stream = new ReadableStream($this->collectionWrapper, $fileDocument); @@ -198,7 +190,7 @@ public function testSeekBeforeReading() $this->assertSame('ij', $stream->readBytes(2)); } - public function testSeekOutOfRange() + public function testSeekOutOfRange(): void { $fileDocument = $this->collectionWrapper->findFileById('length-10'); $stream = new ReadableStream($this->collectionWrapper, $fileDocument); @@ -208,10 +200,8 @@ public function testSeekOutOfRange() $stream->seek(11); } - /** - * @dataProvider providePreviousChunkSeekOffsetAndBytes - */ - public function testSeekPreviousChunk($offset, $length, $expectedBytes) + /** @dataProvider providePreviousChunkSeekOffsetAndBytes */ + public function testSeekPreviousChunk($offset, $length, $expectedBytes): void { $fileDocument = $this->collectionWrapper->findFileById('length-10'); $stream = new ReadableStream($this->collectionWrapper, $fileDocument); @@ -222,11 +212,11 @@ public function testSeekPreviousChunk($offset, $length, $expectedBytes) $commands = []; (new CommandObserver())->observe( - function () use ($stream, $offset, $length, $expectedBytes) { + function () use ($stream, $offset, $length, $expectedBytes): void { $stream->seek($offset); $this->assertSame($expectedBytes, $stream->readBytes($length)); }, - function (array $event) use (&$commands) { + function (array $event) use (&$commands): void { $commands[] = $event['started']->getCommandName(); } ); @@ -244,10 +234,8 @@ public function providePreviousChunkSeekOffsetAndBytes() ]; } - /** - * @dataProvider provideSameChunkSeekOffsetAndBytes - */ - public function testSeekSameChunk($offset, $length, $expectedBytes) + /** @dataProvider provideSameChunkSeekOffsetAndBytes */ + public function testSeekSameChunk($offset, $length, $expectedBytes): void { $fileDocument = $this->collectionWrapper->findFileById('length-10'); $stream = new ReadableStream($this->collectionWrapper, $fileDocument); @@ -258,11 +246,11 @@ public function testSeekSameChunk($offset, $length, $expectedBytes) $commands = []; (new CommandObserver())->observe( - function () use ($stream, $offset, $length, $expectedBytes) { + function () use ($stream, $offset, $length, $expectedBytes): void { $stream->seek($offset); $this->assertSame($expectedBytes, $stream->readBytes($length)); }, - function (array $event) use (&$commands) { + function (array $event) use (&$commands): void { $commands[] = $event['started']->getCommandName(); } ); @@ -278,10 +266,8 @@ public function provideSameChunkSeekOffsetAndBytes() ]; } - /** - * @dataProvider provideSubsequentChunkSeekOffsetAndBytes - */ - public function testSeekSubsequentChunk($offset, $length, $expectedBytes) + /** @dataProvider provideSubsequentChunkSeekOffsetAndBytes */ + public function testSeekSubsequentChunk($offset, $length, $expectedBytes): void { $fileDocument = $this->collectionWrapper->findFileById('length-10'); $stream = new ReadableStream($this->collectionWrapper, $fileDocument); @@ -292,11 +278,11 @@ public function testSeekSubsequentChunk($offset, $length, $expectedBytes) $commands = []; (new CommandObserver())->observe( - function () use ($stream, $offset, $length, $expectedBytes) { + function () use ($stream, $offset, $length, $expectedBytes): void { $stream->seek($offset); $this->assertSame($expectedBytes, $stream->readBytes($length)); }, - function (array $event) use (&$commands) { + function (array $event) use (&$commands): void { $commands[] = $event['started']->getCommandName(); } ); diff --git a/tests/GridFS/SpecFunctionalTest.php b/tests/GridFS/SpecFunctionalTest.php deleted file mode 100644 index f9d42f3f8..000000000 --- a/tests/GridFS/SpecFunctionalTest.php +++ /dev/null @@ -1,389 +0,0 @@ -expectedFilesCollection = new Collection($this->manager, $this->getDatabaseName(), 'expected.files'); - $this->expectedFilesCollection->drop(); - - $this->expectedChunksCollection = new Collection($this->manager, $this->getDatabaseName(), 'expected.chunks'); - $this->expectedChunksCollection->drop(); - } - - /** - * @dataProvider provideSpecificationTests - */ - public function testSpecification(array $initialData, array $test) - { - $this->initializeData($initialData); - - if (isset($test['arrange'])) { - foreach ($test['arrange']['data'] as $dataModification) { - $this->executeDataModification($dataModification); - } - } - - try { - $result = $this->executeAct($test['act']); - } catch (Throwable $e) { - $result = $e; - } - - /* Per the GridFS spec: "Drivers MAY attempt to delete any orphaned - * chunks with files_id equal to id before raising the error." The spec - * tests do not expect orphaned chunks to be removed, so we manually - * remove those chunks from the expected collection. */ - if ($test['act']['operation'] === 'delete' && $result instanceof FileNotFoundException) { - $filesId = $this->convertTypes($test['act'])['arguments']['id']; - $this->expectedChunksCollection->deleteMany(['files_id' => $filesId]); - } - - if (isset($test['assert'])) { - $this->executeAssert($test['assert'], $result); - } - } - - public function provideSpecificationTests() - { - $testArgs = []; - - foreach (glob(__DIR__ . '/spec-tests/*.json') as $filename) { - $json = json_decode(file_get_contents($filename), true); - - foreach ($json['tests'] as $test) { - $name = str_replace(' ', '_', $test['description']); - $testArgs[$name] = [$json['data'], $test]; - } - } - - return $testArgs; - } - - /** - * Assert that the collections contain equivalent documents. - * - * This method will resolve references within the expected collection's - * documents before comparing documents. Occurrences of "*result" in the - * expected collection's documents will be replaced with the actual result. - * Occurrences of "*actual" in the expected collection's documents will be - * replaced with the corresponding value in the actual collection's document - * being compared. - * - * @param Collection $expectedCollection - * @param Collection $actualCollection - * @param mixed $actualResult - */ - private function assertEquivalentCollections($expectedCollection, $actualCollection, $actualResult) - { - $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); - $mi->attachIterator(new IteratorIterator($expectedCollection->find())); - $mi->attachIterator(new IteratorIterator($actualCollection->find())); - - foreach ($mi as $documents) { - list($expectedDocument, $actualDocument) = $documents; - - foreach ($expectedDocument as $key => $value) { - if (! is_string($value)) { - continue; - } - - if ($value === '*result') { - $expectedDocument[$key] = $actualResult; - } - - if (! strncmp($value, '*actual_', 8)) { - $expectedDocument[$key] = $actualDocument[$key]; - } - } - - $this->assertSameDocument($expectedDocument, $actualDocument); - } - } - - /** - * Convert encoded types in the array and return the modified array. - * - * Nested arrays with "$oid" and "$date" keys will be converted to ObjectId - * and UTCDateTime instances, respectively. Nested arrays with "$hex" keys - * will be converted to a string or Binary object. - * - * @param param $data - * @param boolean $createBinary If true, convert "$hex" values to a Binary - * @return array - */ - private function convertTypes(array $data, $createBinary = true) - { - /* array_walk_recursive() only visits leaf nodes within the array, so we - * need to manually recurse. - */ - array_walk($data, function (&$value) use ($createBinary) { - if (! is_array($value)) { - return; - } - - if (isset($value['$oid'])) { - $value = new ObjectId($value['$oid']); - - return; - } - - if (isset($value['$hex'])) { - $value = $createBinary - ? new Binary(hex2bin($value['$hex']), Binary::TYPE_GENERIC) - : hex2bin($value['$hex']); - - return; - } - - if (isset($value['$date'])) { - $value = new UTCDateTime(new DateTime($value['$date'])); - - return; - } - - $value = $this->convertTypes($value, $createBinary); - }); - - return $data; - } - - /** - * Executes an "act" block. - * - * @param array $act - * @return mixed - * @throws LogicException if the operation is unsupported - */ - private function executeAct(array $act) - { - $act = $this->convertTypes($act, false); - - switch ($act['operation']) { - case 'delete': - return $this->bucket->delete($act['arguments']['id']); - case 'download': - return stream_get_contents($this->bucket->openDownloadStream($act['arguments']['id'])); - case 'download_by_name': - return stream_get_contents($this->bucket->openDownloadStreamByName( - $act['arguments']['filename'], - $act['arguments']['options'] ?? [] - )); - case 'upload': - return $this->bucket->uploadFromStream( - $act['arguments']['filename'], - $this->createStream($act['arguments']['source']), - $act['arguments']['options'] ?? [] - ); - default: - throw new LogicException('Unsupported act: ' . $act['operation']); - } - } - - /** - * Executes an "assert" block. - * - * @param array $assert - * @param mixed $actualResult - * @return mixed - * @throws LogicException if the operation is unsupported - */ - private function executeAssert(array $assert, $actualResult) - { - if (isset($assert['error'])) { - $this->assertInstanceOf($this->getExceptionClassForError($assert['error']), $actualResult); - } - - if (isset($assert['result'])) { - $this->executeAssertResult($assert['result'], $actualResult); - } - - if (! isset($assert['data'])) { - return; - } - - /* Since "*actual" may be used for an expected document's "_id", append - * a unique value to avoid duplicate key exceptions. - */ - array_walk_recursive($assert['data'], function (&$value) { - if ($value === '*actual') { - $value .= '_' . new ObjectId(); - } - }); - - foreach ($assert['data'] as $dataModification) { - $this->executeDataModification($dataModification); - } - - $this->assertEquivalentCollections($this->expectedFilesCollection, $this->filesCollection, $actualResult); - $this->assertEquivalentCollections($this->expectedChunksCollection, $this->chunksCollection, $actualResult); - } - - /** - * Executes the "result" section of an "assert" block. - * - * @param mixed $expectedResult - * @param mixed $actualResult - * @throws LogicException if the result assertion is unsupported - */ - private function executeAssertResult($expectedResult, $actualResult) - { - if ($expectedResult === 'void') { - return $this->assertNull($actualResult); - } - - if ($expectedResult === '&result') { - // Do nothing; assertEquivalentCollections() will handle this - return; - } - - if (isset($expectedResult['$hex'])) { - return $this->assertSame(hex2bin($expectedResult['$hex']), $actualResult); - } - - throw new LogicException('Unsupported result assertion: ' . var_export($expectedResult, true)); - } - - /** - * Executes a data modification from an "arrange" or "assert" block. - * - * @param array $dataModification - * @return mixed - * @throws LogicException if the operation or collection is unsupported - */ - private function executeDataModification(array $dataModification) - { - if (empty($dataModification)) { - throw new LogicException('Command for data modification is empty'); - } - - foreach ($dataModification as $type => $collectionName) { - break; - } - - if (! in_array($collectionName, ['fs.files', 'fs.chunks', 'expected.files', 'expected.chunks'])) { - throw new LogicException('Unsupported collection: ' . $collectionName); - } - - $dataModification = $this->convertTypes($dataModification); - $operations = []; - - switch ($type) { - case 'delete': - foreach ($dataModification['deletes'] as $delete) { - $operations[] = [ ($delete['limit'] === 1 ? 'deleteOne' : 'deleteMany') => [ $delete['q'] ] ]; - } - - break; - - case 'insert': - foreach ($dataModification['documents'] as $document) { - $operations[] = [ 'insertOne' => [ $document ] ]; - } - - break; - - case 'update': - foreach ($dataModification['updates'] as $update) { - $operations[] = [ 'updateOne' => [ $update['q'], $update['u'] ] ]; - } - - break; - - default: - throw new LogicException('Unsupported arrangement: ' . $type); - } - - $bulk = new BulkWrite($this->getDatabaseName(), $collectionName, $operations); - - return $bulk->execute($this->getPrimaryServer()); - } - - /** - * Returns the exception class for the "error" section of an "assert" block. - * - * @param string $error - * @return string - * @throws LogicException if the error is unsupported - */ - private function getExceptionClassForError($error) - { - switch ($error) { - case 'FileNotFound': - case 'RevisionNotFound': - return FileNotFoundException::class; - case 'ChunkIsMissing': - case 'ChunkIsWrongSize': - /* Although ReadableStream throws a CorruptFileException, the - * stream wrapper will convert it to a PHP error of type - * E_USER_WARNING. */ - return Warning::class; - default: - throw new LogicException('Unsupported error: ' . $error); - } - } - - /** - * Initializes data in the files and chunks collections. - * - * @param array $data - */ - private function initializeData(array $data) - { - $data = $this->convertTypes($data); - - if (! empty($data['files'])) { - $this->filesCollection->insertMany($data['files']); - $this->expectedFilesCollection->insertMany($data['files']); - } - - if (! empty($data['chunks'])) { - $this->chunksCollection->insertMany($data['chunks']); - $this->expectedChunksCollection->insertMany($data['chunks']); - } - } -} diff --git a/tests/GridFS/StreamWrapperFunctionalTest.php b/tests/GridFS/StreamWrapperFunctionalTest.php index 88e1bd9b8..792338d36 100644 --- a/tests/GridFS/StreamWrapperFunctionalTest.php +++ b/tests/GridFS/StreamWrapperFunctionalTest.php @@ -4,13 +4,14 @@ use MongoDB\BSON\Binary; use MongoDB\BSON\UTCDateTime; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; + use function fclose; use function feof; use function fread; use function fseek; use function fstat; use function fwrite; + use const SEEK_CUR; use const SEEK_END; use const SEEK_SET; @@ -20,9 +21,7 @@ */ class StreamWrapperFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; - - private function doSetUp() + public function setUp(): void { parent::setUp(); @@ -37,14 +36,14 @@ private function doSetUp() ]); } - public function testReadableStreamClose() + public function testReadableStreamClose(): void { $stream = $this->bucket->openDownloadStream('length-10'); $this->assertTrue(fclose($stream)); } - public function testReadableStreamEof() + public function testReadableStreamEof(): void { $stream = $this->bucket->openDownloadStream('length-10'); @@ -53,7 +52,7 @@ public function testReadableStreamEof() $this->assertTrue(feof($stream)); } - public function testReadableStreamRead() + public function testReadableStreamRead(): void { $stream = $this->bucket->openDownloadStream('length-10'); @@ -62,7 +61,7 @@ public function testReadableStreamRead() $this->assertSame('', fread($stream, 3)); } - public function testReadableStreamSeek() + public function testReadableStreamSeek(): void { $stream = $this->bucket->openDownloadStream('length-10'); @@ -88,7 +87,7 @@ public function testReadableStreamSeek() $this->assertSame(-1, fseek($stream, 1, SEEK_END)); } - public function testReadableStreamStat() + public function testReadableStreamStat(): void { $stream = $this->bucket->openDownloadStream('length-10'); @@ -105,14 +104,14 @@ public function testReadableStreamStat() $this->assertSame(4, $stat['blksize']); } - public function testReadableStreamWrite() + public function testReadableStreamWrite(): void { $stream = $this->bucket->openDownloadStream('length-10'); $this->assertSame(0, fwrite($stream, 'foobar')); } - public function testWritableStreamClose() + public function testWritableStreamClose(): void { $stream = $this->bucket->openUploadStream('filename'); @@ -122,7 +121,7 @@ public function testWritableStreamClose() $this->assertStreamContents('foobar', $this->bucket->openDownloadStreamByName('filename')); } - public function testWritableStreamEof() + public function testWritableStreamEof(): void { $stream = $this->bucket->openUploadStream('filename'); @@ -131,7 +130,7 @@ public function testWritableStreamEof() $this->assertFalse(feof($stream)); } - public function testWritableStreamRead() + public function testWritableStreamRead(): void { $stream = $this->bucket->openUploadStream('filename'); @@ -140,7 +139,7 @@ public function testWritableStreamRead() $this->assertSame('', fread($stream, 8192)); } - public function testWritableStreamSeek() + public function testWritableStreamSeek(): void { $stream = $this->bucket->openUploadStream('filename'); @@ -159,7 +158,7 @@ public function testWritableStreamSeek() $this->assertSame(-1, fseek($stream, 1, SEEK_END)); } - public function testWritableStreamStatBeforeSaving() + public function testWritableStreamStatBeforeSaving(): void { $stream = $this->bucket->openUploadStream('filename', ['chunkSizeBytes' => 1024]); @@ -182,7 +181,7 @@ public function testWritableStreamStatBeforeSaving() $this->assertSame(6, $stat['size']); } - public function testWritableStreamStatAfterSaving() + public function testWritableStreamStatAfterSaving(): void { $stream = $this->bucket->openDownloadStream('length-10'); @@ -199,7 +198,7 @@ public function testWritableStreamStatAfterSaving() $this->assertSame(4, $stat['blksize']); } - public function testWritableStreamWrite() + public function testWritableStreamWrite(): void { $stream = $this->bucket->openUploadStream('filename'); diff --git a/tests/GridFS/UnusableStream.php b/tests/GridFS/UnusableStream.php index f7c7970ce..34e964da4 100644 --- a/tests/GridFS/UnusableStream.php +++ b/tests/GridFS/UnusableStream.php @@ -6,21 +6,24 @@ use function stream_get_wrappers; use function stream_wrapper_register; use function stream_wrapper_unregister; + use const SEEK_SET; use const STREAM_IS_URL; final class UnusableStream { - public static function register($protocol = 'unusable') + public $context; + + public static function register($protocol = 'unusable'): void { if (in_array($protocol, stream_get_wrappers())) { stream_wrapper_unregister($protocol); } - stream_wrapper_register($protocol, static::class, STREAM_IS_URL); + stream_wrapper_register($protocol, self::class, STREAM_IS_URL); } - public function stream_close() + public function stream_close(): void { } diff --git a/tests/GridFS/WritableStreamFunctionalTest.php b/tests/GridFS/WritableStreamFunctionalTest.php index cadee3ad6..6a11f64d4 100644 --- a/tests/GridFS/WritableStreamFunctionalTest.php +++ b/tests/GridFS/WritableStreamFunctionalTest.php @@ -5,7 +5,7 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\GridFS\CollectionWrapper; use MongoDB\GridFS\WritableStream; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; + use function str_repeat; /** @@ -13,22 +13,18 @@ */ class WritableStreamFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; - /** @var CollectionWrapper */ private $collectionWrapper; - private function doSetUp() + public function setUp(): void { parent::setUp(); $this->collectionWrapper = new CollectionWrapper($this->manager, $this->getDatabaseName(), 'fs'); } - /** - * @doesNotPerformAssertions - */ - public function testValidConstructorOptions() + /** @doesNotPerformAssertions */ + public function testValidConstructorOptions(): void { new WritableStream($this->collectionWrapper, 'filename', [ '_id' => 'custom-id', @@ -37,10 +33,8 @@ public function testValidConstructorOptions() ]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new WritableStream($this->collectionWrapper, 'filename', $options); @@ -65,14 +59,14 @@ public function provideInvalidConstructorOptions() return $options; } - public function testConstructorShouldRequireChunkSizeBytesOptionToBePositive() + public function testConstructorShouldRequireChunkSizeBytesOptionToBePositive(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Expected "chunkSizeBytes" option to be >= 1, 0 given'); new WritableStream($this->collectionWrapper, 'filename', ['chunkSizeBytes' => 0]); } - public function testWriteBytesAlwaysUpdatesFileSize() + public function testWriteBytesAlwaysUpdatesFileSize(): void { $stream = new WritableStream($this->collectionWrapper, 'filename', ['chunkSizeBytes' => 1024]); @@ -88,10 +82,8 @@ public function testWriteBytesAlwaysUpdatesFileSize() $this->assertSame(1536, $stream->getSize()); } - /** - * @dataProvider provideInputDataAndExpectedMD5 - */ - public function testWriteBytesCalculatesMD5($input, $expectedMD5) + /** @dataProvider provideInputDataAndExpectedMD5 */ + public function testWriteBytesCalculatesMD5($input, $expectedMD5): void { $stream = new WritableStream($this->collectionWrapper, 'filename'); $stream->writeBytes($input); diff --git a/tests/GridFS/spec-tests/delete.json b/tests/GridFS/spec-tests/delete.json deleted file mode 100644 index d74e49284..000000000 --- a/tests/GridFS/spec-tests/delete.json +++ /dev/null @@ -1,291 +0,0 @@ -{ - "data": { - "files": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0-with-empty-chunk", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "length": 2, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "c700ed4fdb1d27055aa3faa2c2432283", - "filename": "length-2", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "length": 8, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "dd254cdc958e53abaa67da9f797125f5", - "filename": "length-8", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ], - "chunks": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000002" - }, - "n": 0, - "data": { - "$hex": "" - } - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000003" - }, - "n": 0, - "data": { - "$hex": "1122" - } - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 0, - "data": { - "$hex": "11223344" - } - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 1, - "data": { - "$hex": "55667788" - } - } - ] - }, - "tests": [ - { - "description": "Delete when length is 0", - "act": { - "operation": "delete", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - }, - "assert": { - "result": "void", - "data": [ - { - "delete": "expected.files", - "deletes": [ - { - "q": { - "_id": { - "$oid": "000000000000000000000001" - } - }, - "limit": 1 - } - ] - } - ] - } - }, - { - "description": "Delete when length is 0 and there is one extra empty chunk", - "act": { - "operation": "delete", - "arguments": { - "id": { - "$oid": "000000000000000000000002" - } - } - }, - "assert": { - "result": "void", - "data": [ - { - "delete": "expected.files", - "deletes": [ - { - "q": { - "_id": { - "$oid": "000000000000000000000002" - } - }, - "limit": 1 - } - ] - }, - { - "delete": "expected.chunks", - "deletes": [ - { - "q": { - "files_id": { - "$oid": "000000000000000000000002" - } - }, - "limit": 0 - } - ] - } - ] - } - }, - { - "description": "Delete when length is 8", - "act": { - "operation": "delete", - "arguments": { - "id": { - "$oid": "000000000000000000000004" - } - } - }, - "assert": { - "result": "void", - "data": [ - { - "delete": "expected.files", - "deletes": [ - { - "q": { - "_id": { - "$oid": "000000000000000000000004" - } - }, - "limit": 1 - } - ] - }, - { - "delete": "expected.chunks", - "deletes": [ - { - "q": { - "files_id": { - "$oid": "000000000000000000000004" - } - }, - "limit": 0 - } - ] - } - ] - } - }, - { - "description": "Delete when files entry does not exist", - "act": { - "operation": "delete", - "arguments": { - "id": { - "$oid": "000000000000000000000000" - } - } - }, - "assert": { - "error": "FileNotFound" - } - }, - { - "description": "Delete when files entry does not exist and there are orphaned chunks", - "arrange": { - "data": [ - { - "delete": "fs.files", - "deletes": [ - { - "q": { - "_id": { - "$oid": "000000000000000000000004" - } - }, - "limit": 1 - } - ] - } - ] - }, - "act": { - "operation": "delete", - "arguments": { - "id": { - "$oid": "000000000000000000000004" - } - } - }, - "assert": { - "error": "FileNotFound", - "data": [ - { - "delete": "expected.files", - "deletes": [ - { - "q": { - "_id": { - "$oid": "000000000000000000000004" - } - }, - "limit": 1 - } - ] - } - ] - } - } - ] -} diff --git a/tests/GridFS/spec-tests/download.json b/tests/GridFS/spec-tests/download.json deleted file mode 100644 index 5092fba98..000000000 --- a/tests/GridFS/spec-tests/download.json +++ /dev/null @@ -1,467 +0,0 @@ -{ - "data": { - "files": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0-with-empty-chunk", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "length": 2, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "c700ed4fdb1d27055aa3faa2c2432283", - "filename": "length-2", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "length": 8, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "dd254cdc958e53abaa67da9f797125f5", - "filename": "length-8", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000005" - }, - "length": 10, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "57d83cd477bfb1ccd975ab33d827a92b", - "filename": "length-10", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000006" - }, - "length": 2, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "c700ed4fdb1d27055aa3faa2c2432283", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ], - "chunks": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000002" - }, - "n": 0, - "data": { - "$hex": "" - } - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000003" - }, - "n": 0, - "data": { - "$hex": "1122" - } - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 0, - "data": { - "$hex": "11223344" - } - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 1, - "data": { - "$hex": "55667788" - } - }, - { - "_id": { - "$oid": "000000000000000000000005" - }, - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 0, - "data": { - "$hex": "11223344" - } - }, - { - "_id": { - "$oid": "000000000000000000000006" - }, - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 1, - "data": { - "$hex": "55667788" - } - }, - { - "_id": { - "$oid": "000000000000000000000007" - }, - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 2, - "data": { - "$hex": "99aa" - } - }, - { - "_id": { - "$oid": "000000000000000000000008" - }, - "files_id": { - "$oid": "000000000000000000000006" - }, - "n": 0, - "data": { - "$hex": "1122" - } - } - ] - }, - "tests": [ - { - "description": "Download when length is zero", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "" - } - } - }, - { - "description": "Download when length is zero and there is one empty chunk", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000002" - }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "" - } - } - }, - { - "description": "Download when there is one chunk", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000003" - }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "1122" - } - } - }, - { - "description": "Download when there are two chunks", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000004" - }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "1122334455667788" - } - } - }, - { - "description": "Download when there are three chunks", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "112233445566778899aa" - } - } - }, - { - "description": "Download when files entry does not exist", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000000" - }, - "options": {} - } - }, - "assert": { - "error": "FileNotFound" - } - }, - { - "description": "Download when an intermediate chunk is missing", - "arrange": { - "data": [ - { - "delete": "fs.chunks", - "deletes": [ - { - "q": { - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 1 - }, - "limit": 1 - } - ] - } - ] - }, - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - } - }, - "assert": { - "error": "ChunkIsMissing" - } - }, - { - "description": "Download when final chunk is missing", - "arrange": { - "data": [ - { - "delete": "fs.chunks", - "deletes": [ - { - "q": { - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 1 - }, - "limit": 1 - } - ] - } - ] - }, - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - } - }, - "assert": { - "error": "ChunkIsMissing" - } - }, - { - "description": "Download when an intermediate chunk is the wrong size", - "arrange": { - "data": [ - { - "update": "fs.chunks", - "updates": [ - { - "q": { - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 1 - }, - "u": { - "$set": { - "data": { - "$hex": "556677" - } - } - } - }, - { - "q": { - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 2 - }, - "u": { - "$set": { - "data": { - "$hex": "8899aa" - } - } - } - } - ] - } - ] - }, - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - } - }, - "assert": { - "error": "ChunkIsWrongSize" - } - }, - { - "description": "Download when final chunk is the wrong size", - "arrange": { - "data": [ - { - "update": "fs.chunks", - "updates": [ - { - "q": { - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 2 - }, - "u": { - "$set": { - "data": { - "$hex": "99" - } - } - } - } - ] - } - ] - }, - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - } - }, - "assert": { - "error": "ChunkIsWrongSize" - } - }, - { - "description": "Download legacy file with no name", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000006" - }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "1122" - } - } - } - ] -} diff --git a/tests/GridFS/spec-tests/download_by_name.json b/tests/GridFS/spec-tests/download_by_name.json deleted file mode 100644 index ecc8c9e2c..000000000 --- a/tests/GridFS/spec-tests/download_by_name.json +++ /dev/null @@ -1,240 +0,0 @@ -{ - "data": { - "files": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "47ed733b8d10be225eceba344d533586", - "filename": "abc", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-02T00:00:00.000Z" - }, - "md5": "b15835f133ff2e27c7cb28117bfae8f4", - "filename": "abc", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-03T00:00:00.000Z" - }, - "md5": "eccbc87e4b5ce2fe28308fd9f2a7baf3", - "filename": "abc", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-04T00:00:00.000Z" - }, - "md5": "f623e75af30e62bbd73d6df5b50bb7b5", - "filename": "abc", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000005" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-05T00:00:00.000Z" - }, - "md5": "4c614360da93c0a041b22e537de151eb", - "filename": "abc", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ], - "chunks": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000001" - }, - "n": 0, - "data": { - "$hex": "11" - } - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000002" - }, - "n": 0, - "data": { - "$hex": "22" - } - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "files_id": { - "$oid": "000000000000000000000003" - }, - "n": 0, - "data": { - "$hex": "33" - } - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 0, - "data": { - "$hex": "44" - } - }, - { - "_id": { - "$oid": "000000000000000000000005" - }, - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 0, - "data": { - "$hex": "55" - } - } - ] - }, - "tests": [ - { - "description": "Download_by_name when revision is 0", - "act": { - "operation": "download_by_name", - "arguments": { - "filename": "abc", - "options": { - "revision": 0 - } - } - }, - "assert": { - "result": { - "$hex": "11" - } - } - }, - { - "description": "Download_by_name when revision is 1", - "act": { - "operation": "download_by_name", - "arguments": { - "filename": "abc", - "options": { - "revision": 1 - } - } - }, - "assert": { - "result": { - "$hex": "22" - } - } - }, - { - "description": "Download_by_name when revision is -2", - "act": { - "operation": "download_by_name", - "arguments": { - "filename": "abc", - "options": { - "revision": -2 - } - } - }, - "assert": { - "result": { - "$hex": "44" - } - } - }, - { - "description": "Download_by_name when revision is -1", - "act": { - "operation": "download_by_name", - "arguments": { - "filename": "abc", - "options": { - "revision": -1 - } - } - }, - "assert": { - "result": { - "$hex": "55" - } - } - }, - { - "description": "Download_by_name when files entry does not exist", - "act": { - "operation": "download_by_name", - "arguments": { - "filename": "xyz" - } - }, - "assert": { - "error": "FileNotFound" - } - }, - { - "description": "Download_by_name when revision does not exist", - "act": { - "operation": "download_by_name", - "arguments": { - "filename": "abc", - "options": { - "revision": 999 - } - } - }, - "assert": { - "error": "RevisionNotFound" - } - } - ] -} diff --git a/tests/GridFS/spec-tests/upload.json b/tests/GridFS/spec-tests/upload.json deleted file mode 100644 index ecec0c548..000000000 --- a/tests/GridFS/spec-tests/upload.json +++ /dev/null @@ -1,466 +0,0 @@ -{ - "data": { - "files": [], - "chunks": [] - }, - "tests": [ - { - "description": "Upload when length is 0", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "" - }, - "options": { - "chunkSizeBytes": 4 - } - } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 0, - "chunkSize": 4, - "uploadDate": "*actual", - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "filename" - } - ] - } - ] - } - }, - { - "description": "Upload when length is 1", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "11" - }, - "options": { - "chunkSizeBytes": 4 - } - } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 1, - "chunkSize": 4, - "uploadDate": "*actual", - "md5": "47ed733b8d10be225eceba344d533586", - "filename": "filename" - } - ] - }, - { - "insert": "expected.chunks", - "documents": [ - { - "_id": "*actual", - "files_id": "*result", - "n": 0, - "data": { - "$hex": "11" - } - } - ] - } - ] - } - }, - { - "description": "Upload when length is 3", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "112233" - }, - "options": { - "chunkSizeBytes": 4 - } - } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 3, - "chunkSize": 4, - "uploadDate": "*actual", - "md5": "bafae3a174ab91fc70db7a6aa50f4f52", - "filename": "filename" - } - ] - }, - { - "insert": "expected.chunks", - "documents": [ - { - "_id": "*actual", - "files_id": "*result", - "n": 0, - "data": { - "$hex": "112233" - } - } - ] - } - ] - } - }, - { - "description": "Upload when length is 4", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "11223344" - }, - "options": { - "chunkSizeBytes": 4 - } - } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 4, - "chunkSize": 4, - "uploadDate": "*actual", - "md5": "7e7c77cff5705d1f7574a25ef6662117", - "filename": "filename" - } - ] - }, - { - "insert": "expected.chunks", - "documents": [ - { - "_id": "*actual", - "files_id": "*result", - "n": 0, - "data": { - "$hex": "11223344" - } - } - ] - } - ] - } - }, - { - "description": "Upload when length is 5", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "1122334455" - }, - "options": { - "chunkSizeBytes": 4 - } - } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 5, - "chunkSize": 4, - "uploadDate": "*actual", - "md5": "283d4fea5dded59cf837d3047328f5af", - "filename": "filename" - } - ] - }, - { - "insert": "expected.chunks", - "documents": [ - { - "_id": "*actual", - "files_id": "*result", - "n": 0, - "data": { - "$hex": "11223344" - } - }, - { - "_id": "*actual", - "files_id": "*result", - "n": 1, - "data": { - "$hex": "55" - } - } - ] - } - ] - } - }, - { - "description": "Upload when length is 8", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "1122334455667788" - }, - "options": { - "chunkSizeBytes": 4 - } - } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 8, - "chunkSize": 4, - "uploadDate": "*actual", - "md5": "dd254cdc958e53abaa67da9f797125f5", - "filename": "filename" - } - ] - }, - { - "insert": "expected.chunks", - "documents": [ - { - "_id": "*actual", - "files_id": "*result", - "n": 0, - "data": { - "$hex": "11223344" - } - }, - { - "_id": "*actual", - "files_id": "*result", - "n": 1, - "data": { - "$hex": "55667788" - } - } - ] - } - ] - } - }, - { - "description": "Upload when contentType is provided", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "11" - }, - "options": { - "chunkSizeBytes": 4, - "contentType": "image/jpeg" - } - } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 1, - "chunkSize": 4, - "uploadDate": "*actual", - "md5": "47ed733b8d10be225eceba344d533586", - "filename": "filename", - "contentType": "image/jpeg" - } - ] - }, - { - "insert": "expected.chunks", - "documents": [ - { - "_id": "*actual", - "files_id": "*result", - "n": 0, - "data": { - "$hex": "11" - } - } - ] - } - ] - } - }, - { - "description": "Upload when metadata is provided", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "11" - }, - "options": { - "chunkSizeBytes": 4, - "metadata": { - "x": 1 - } - } - } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 1, - "chunkSize": 4, - "uploadDate": "*actual", - "md5": "47ed733b8d10be225eceba344d533586", - "filename": "filename", - "metadata": { - "x": 1 - } - } - ] - }, - { - "insert": "expected.chunks", - "documents": [ - { - "_id": "*actual", - "files_id": "*result", - "n": 0, - "data": { - "$hex": "11" - } - } - ] - } - ] - } - }, - { - "description": "Upload when length is 0 sans MD5", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "" - }, - "options": { - "chunkSizeBytes": 4, - "disableMD5": true - } - } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 0, - "chunkSize": 4, - "uploadDate": "*actual", - "filename": "filename" - } - ] - } - ] - } - }, - { - "description": "Upload when length is 1 sans MD5", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "11" - }, - "options": { - "chunkSizeBytes": 4, - "disableMD5": true - } - } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 1, - "chunkSize": 4, - "uploadDate": "*actual", - "filename": "filename" - } - ] - }, - { - "insert": "expected.chunks", - "documents": [ - { - "_id": "*actual", - "files_id": "*result", - "n": 0, - "data": { - "$hex": "11" - } - } - ] - } - ] - } - } - ] -} diff --git a/tests/Model/BSONArrayTest.php b/tests/Model/BSONArrayTest.php index 8774d223b..9f420bed9 100644 --- a/tests/Model/BSONArrayTest.php +++ b/tests/Model/BSONArrayTest.php @@ -8,11 +8,12 @@ use MongoDB\Tests\TestCase; use ReflectionClass; use stdClass; + use function json_encode; class BSONArrayTest extends TestCase { - public function testBsonSerializeReindexesKeys() + public function testBsonSerializeReindexesKeys(): void { $data = [0 => 'foo', 2 => 'bar']; @@ -21,7 +22,7 @@ public function testBsonSerializeReindexesKeys() $this->assertSame(['foo', 'bar'], $array->bsonSerialize()); } - public function testClone() + public function testClone(): void { $array = new BSONArray([ [ @@ -46,7 +47,7 @@ public function testClone() $this->assertNotSame($array[1][2][1], $arrayClone[1][2][1]); } - public function testCloneRespectsUncloneableObjects() + public function testCloneRespectsUncloneableObjects(): void { $this->assertFalse((new ReflectionClass(UncloneableObject::class))->isCloneable()); @@ -62,7 +63,7 @@ public function testCloneRespectsUncloneableObjects() $this->assertSame($array[1][0], $arrayClone[1][0]); } - public function testCloneSupportsBSONTypes() + public function testCloneSupportsBSONTypes(): void { /* Note: this test does not check that the BSON type itself is cloned, * as that is not yet supported in the driver (see: PHPC-1230). */ @@ -76,7 +77,7 @@ public function testCloneSupportsBSONTypes() $this->assertNotSame($array[1], $arrayClone[1]); } - public function testJsonSerialize() + public function testJsonSerialize(): void { $document = new BSONArray([ 'foo', @@ -90,7 +91,7 @@ public function testJsonSerialize() $this->assertSame($expectedJson, json_encode($document)); } - public function testJsonSerializeReindexesKeys() + public function testJsonSerializeReindexesKeys(): void { $data = [0 => 'foo', 2 => 'bar']; @@ -99,7 +100,7 @@ public function testJsonSerializeReindexesKeys() $this->assertSame(['foo', 'bar'], $array->jsonSerialize()); } - public function testSetState() + public function testSetState(): void { $data = ['foo', 'bar']; diff --git a/tests/Model/BSONDocumentTest.php b/tests/Model/BSONDocumentTest.php index ad095e31f..bd0009dcf 100644 --- a/tests/Model/BSONDocumentTest.php +++ b/tests/Model/BSONDocumentTest.php @@ -9,18 +9,19 @@ use MongoDB\Tests\TestCase; use ReflectionClass; use stdClass; + use function json_encode; class BSONDocumentTest extends TestCase { - public function testConstructorDefaultsToPropertyAccess() + public function testConstructorDefaultsToPropertyAccess(): void { $document = new BSONDocument(['foo' => 'bar']); $this->assertEquals(ArrayObject::ARRAY_AS_PROPS, $document->getFlags()); $this->assertSame('bar', $document->foo); } - public function testBsonSerializeCastsToObject() + public function testBsonSerializeCastsToObject(): void { $data = [0 => 'foo', 2 => 'bar']; @@ -29,7 +30,7 @@ public function testBsonSerializeCastsToObject() $this->assertEquals((object) [0 => 'foo', 2 => 'bar'], $document->bsonSerialize()); } - public function testClone() + public function testClone(): void { $document = new BSONDocument([ 'a' => [ @@ -54,7 +55,7 @@ public function testClone() $this->assertNotSame($document['b']['c'][1], $documentClone['b']['c'][1]); } - public function testCloneRespectsUncloneableObjects() + public function testCloneRespectsUncloneableObjects(): void { $this->assertFalse((new ReflectionClass(UncloneableObject::class))->isCloneable()); @@ -70,7 +71,7 @@ public function testCloneRespectsUncloneableObjects() $this->assertSame($document['b']['a'], $documentClone['b']['a']); } - public function testCloneSupportsBSONTypes() + public function testCloneSupportsBSONTypes(): void { /* Note: this test does not check that the BSON type itself is cloned, * as that is not yet supported in the driver (see: PHPC-1230). */ @@ -84,7 +85,7 @@ public function testCloneSupportsBSONTypes() $this->assertNotSame($document['b'], $documentClone['b']); } - public function testJsonSerialize() + public function testJsonSerialize(): void { $document = new BSONDocument([ 'foo' => 'bar', @@ -98,7 +99,7 @@ public function testJsonSerialize() $this->assertSame($expectedJson, json_encode($document)); } - public function testJsonSerializeCastsToObject() + public function testJsonSerializeCastsToObject(): void { $data = [0 => 'foo', 2 => 'bar']; @@ -107,7 +108,7 @@ public function testJsonSerializeCastsToObject() $this->assertEquals((object) [0 => 'foo', 2 => 'bar'], $document->jsonSerialize()); } - public function testSetState() + public function testSetState(): void { $data = ['foo' => 'bar']; diff --git a/tests/Model/BSONIteratorTest.php b/tests/Model/BSONIteratorTest.php index c48ed393d..9e208151b 100644 --- a/tests/Model/BSONIteratorTest.php +++ b/tests/Model/BSONIteratorTest.php @@ -5,6 +5,7 @@ use MongoDB\Exception\UnexpectedValueException; use MongoDB\Model\BSONIterator; use MongoDB\Tests\TestCase; + use function array_map; use function implode; use function iterator_to_array; @@ -13,10 +14,8 @@ class BSONIteratorTest extends TestCase { - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ - public function testValidValues(array $typeMap = null, $binaryString, array $expectedDocuments) + /** @dataProvider provideTypeMapOptionsAndExpectedDocuments */ + public function testValidValues(?array $typeMap, $binaryString, array $expectedDocuments): void { $bsonIt = new BSONIterator($binaryString, ['typeMap' => $typeMap]); @@ -87,7 +86,7 @@ public function provideTypeMapOptionsAndExpectedDocuments() ]; } - public function testCannotReadLengthFromFirstDocument() + public function testCannotReadLengthFromFirstDocument(): void { $binaryString = substr(fromPHP([]), 0, 3); @@ -98,7 +97,7 @@ public function testCannotReadLengthFromFirstDocument() $bsonIt->rewind(); } - public function testCannotReadLengthFromSubsequentDocument() + public function testCannotReadLengthFromSubsequentDocument(): void { $binaryString = fromPHP([]) . substr(fromPHP([]), 0, 3); @@ -110,7 +109,7 @@ public function testCannotReadLengthFromSubsequentDocument() $bsonIt->next(); } - public function testCannotReadFirstDocument() + public function testCannotReadFirstDocument(): void { $binaryString = substr(fromPHP([]), 0, 4); @@ -121,7 +120,7 @@ public function testCannotReadFirstDocument() $bsonIt->rewind(); } - public function testCannotReadSecondDocument() + public function testCannotReadSecondDocument(): void { $binaryString = fromPHP([]) . substr(fromPHP([]), 0, 4); diff --git a/tests/Model/CachingIteratorTest.php b/tests/Model/CachingIteratorTest.php index f4d4b28bf..1539db3e3 100644 --- a/tests/Model/CachingIteratorTest.php +++ b/tests/Model/CachingIteratorTest.php @@ -3,14 +3,16 @@ namespace MongoDB\Tests\Model; use Exception; +use Iterator; use MongoDB\Model\CachingIterator; use MongoDB\Tests\TestCase; use Throwable; + use function iterator_to_array; class CachingIteratorTest extends TestCase { - public function testTraversingGeneratorConsumesIt() + public function testTraversingGeneratorConsumesIt(): void { $iterator = $this->getTraversable([1, 2, 3]); $this->assertSame([1, 2, 3], iterator_to_array($iterator)); @@ -20,7 +22,7 @@ public function testTraversingGeneratorConsumesIt() iterator_to_array($iterator); } - public function testConstructorRewinds() + public function testConstructorRewinds(): void { $iterator = new CachingIterator($this->getTraversable([1, 2, 3])); @@ -29,7 +31,7 @@ public function testConstructorRewinds() $this->assertSame(1, $iterator->current()); } - public function testIteration() + public function testIteration(): void { $iterator = new CachingIterator($this->getTraversable([1, 2, 3])); @@ -44,7 +46,7 @@ public function testIteration() $this->assertFalse($iterator->valid()); } - public function testIterationWithEmptySet() + public function testIterationWithEmptySet(): void { $iterator = new CachingIterator($this->getTraversable([])); @@ -52,7 +54,7 @@ public function testIterationWithEmptySet() $this->assertFalse($iterator->valid()); } - public function testPartialIterationDoesNotExhaust() + public function testPartialIterationDoesNotExhaust(): void { $traversable = $this->getTraversable([1, 2, new Exception()]); $iterator = new CachingIterator($traversable); @@ -72,7 +74,7 @@ public function testPartialIterationDoesNotExhaust() $this->assertTrue($iterator->valid()); } - public function testRewindAfterPartialIteration() + public function testRewindAfterPartialIteration(): void { $iterator = new CachingIterator($this->getTraversable([1, 2, 3])); @@ -85,13 +87,13 @@ public function testRewindAfterPartialIteration() $this->assertSame([1, 2, 3], iterator_to_array($iterator)); } - public function testCount() + public function testCount(): void { $iterator = new CachingIterator($this->getTraversable([1, 2, 3])); $this->assertCount(3, $iterator); } - public function testCountAfterPartialIteration() + public function testCountAfterPartialIteration(): void { $iterator = new CachingIterator($this->getTraversable([1, 2, 3])); @@ -104,12 +106,72 @@ public function testCountAfterPartialIteration() $this->assertCount(3, $iterator); } - public function testCountWithEmptySet() + public function testCountWithEmptySet(): void { $iterator = new CachingIterator($this->getTraversable([])); $this->assertCount(0, $iterator); } + /** + * This protects against iterators that return valid keys on invalid + * positions, which was the case in ext-mongodb until PHPC-1748 was fixed. + */ + public function testWithWrongIterator(): void + { + $nestedIterator = new class implements Iterator { + /** @var int */ + private $i = 0; + + public function current(): int + { + return $this->i; + } + + public function next(): void + { + $this->i++; + } + + public function key(): int + { + return $this->i; + } + + public function valid(): bool + { + return $this->i == 0; + } + + public function rewind(): void + { + $this->i = 0; + } + }; + + $iterator = new CachingIterator($nestedIterator); + $this->assertCount(1, $iterator); + } + + public function testCountNonUniqueKeys(): void + { + $iterator = new CachingIterator($this->getNonUniqueTraversable([1, 2, 3])); + $this->assertCount(3, $iterator); + } + + public function testIterationNonUniqueKeys(): void + { + $iterator = new CachingIterator($this->getNonUniqueTraversable([1, 2, 3])); + + $expectedItem = 1; + + foreach ($iterator as $key => $item) { + $this->assertSame(0, $key); + $this->assertSame($expectedItem++, $item); + } + + $this->assertFalse($iterator->valid()); + } + private function getTraversable($items) { foreach ($items as $item) { @@ -120,4 +182,15 @@ private function getTraversable($items) } } } + + private function getNonUniqueTraversable($items) + { + foreach ($items as $item) { + if ($item instanceof Exception) { + throw $item; + } else { + yield 0 => $item; + } + } + } } diff --git a/tests/Model/ChangeStreamIteratorTest.php b/tests/Model/ChangeStreamIteratorTest.php index 954ec4efc..c0001d6d7 100644 --- a/tests/Model/ChangeStreamIteratorTest.php +++ b/tests/Model/ChangeStreamIteratorTest.php @@ -1,55 +1,45 @@ getDatabaseName(), $this->getCollectionName()); - $operation->execute($this->getPrimaryServer()); - - $operation = new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), ['capped' => true, 'size' => 8192]); - $operation->execute($this->getPrimaryServer()); - - $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); + // Drop and re-create the collection + $this->collection = $this->createCollection($this->getDatabaseName(), $this->getCollectionName(), ['capped' => true, 'size' => 8192]); } - /** - * @dataProvider provideInvalidIntegerValues - */ - public function testFirstBatchArgumentTypeCheck($firstBatchSize) + /** @dataProvider provideInvalidIntegerValues */ + public function testFirstBatchArgumentTypeCheck($firstBatchSize): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException(TypeError::class); new ChangeStreamIterator($this->collection->find(), $firstBatchSize, null, null); } - public function provideInvalidIntegerValues() - { - return $this->wrapValuesForDataProvider($this->getInvalidIntegerValues()); - } - - public function testInitialResumeToken() + public function testInitialResumeToken(): void { $iterator = new ChangeStreamIterator($this->collection->find(), 0, null, null); $this->assertNull($iterator->getResumeToken()); @@ -61,21 +51,17 @@ public function testInitialResumeToken() $this->assertSameDocument((object) ['resumeToken' => 2], $iterator->getResumeToken()); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testInitialResumeTokenArgumentTypeCheck($initialResumeToken) + /** @dataProvider provideInvalidDocumentValues */ + public function testInitialResumeTokenArgumentTypeCheck($initialResumeToken): void { $this->expectException(InvalidArgumentException::class); new ChangeStreamIterator($this->collection->find(), 0, $initialResumeToken, null); } - /** - * @dataProvider provideInvalidObjectValues - */ - public function testPostBatchResumeTokenArgumentTypeCheck($postBatchResumeToken) + /** @dataProvider provideInvalidObjectValues */ + public function testPostBatchResumeTokenArgumentTypeCheck($postBatchResumeToken): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException(TypeError::class); new ChangeStreamIterator($this->collection->find(), 0, null, $postBatchResumeToken); } @@ -84,7 +70,7 @@ public function provideInvalidObjectValues() return $this->wrapValuesForDataProvider(array_merge($this->getInvalidDocumentValues(), [[]])); } - public function testPostBatchResumeTokenIsReturnedForLastElementInFirstBatch() + public function testPostBatchResumeTokenIsReturnedForLastElementInFirstBatch(): void { $this->collection->insertOne(['_id' => ['resumeToken' => 1], 'x' => 1]); $this->collection->insertOne(['_id' => ['resumeToken' => 2], 'x' => 2]); @@ -93,7 +79,7 @@ public function testPostBatchResumeTokenIsReturnedForLastElementInFirstBatch() $cursor = $this->collection->find([], ['cursorType' => Find::TAILABLE]); $iterator = new ChangeStreamIterator($cursor, 2, null, $postBatchResumeToken); - $this->assertNoCommandExecuted(function () use ($iterator) { + $this->assertNoCommandExecuted(function () use ($iterator): void { $iterator->rewind(); }); $this->assertTrue($iterator->valid()); @@ -106,14 +92,14 @@ public function testPostBatchResumeTokenIsReturnedForLastElementInFirstBatch() $this->assertSameDocument(['_id' => ['resumeToken' => 2], 'x' => 2], $iterator->current()); } - public function testRewindIsNopWhenFirstBatchIsEmpty() + public function testRewindIsNopWhenFirstBatchIsEmpty(): void { $this->collection->insertOne(['_id' => ['resumeToken' => 1], 'x' => 1]); $cursor = $this->collection->find(['x' => ['$gt' => 1]], ['cursorType' => Find::TAILABLE]); $iterator = new ChangeStreamIterator($cursor, 0, null, null); - $this->assertNoCommandExecuted(function () use ($iterator) { + $this->assertNoCommandExecuted(function () use ($iterator): void { $iterator->rewind(); }); $this->assertFalse($iterator->valid()); @@ -128,14 +114,14 @@ public function testRewindIsNopWhenFirstBatchIsEmpty() $iterator->rewind(); } - public function testRewindAdvancesWhenFirstBatchIsNotEmpty() + public function testRewindAdvancesWhenFirstBatchIsNotEmpty(): void { $this->collection->insertOne(['_id' => ['resumeToken' => 1], 'x' => 1]); $cursor = $this->collection->find([], ['cursorType' => Find::TAILABLE]); $iterator = new ChangeStreamIterator($cursor, 1, null, null); - $this->assertNoCommandExecuted(function () use ($iterator) { + $this->assertNoCommandExecuted(function () use ($iterator): void { $iterator->rewind(); }); $this->assertTrue($iterator->valid()); @@ -151,13 +137,13 @@ public function testRewindAdvancesWhenFirstBatchIsNotEmpty() $iterator->rewind(); } - private function assertNoCommandExecuted(callable $callable) + private function assertNoCommandExecuted(callable $callable): void { $commands = []; (new CommandObserver())->observe( $callable, - function (array $event) use (&$commands) { + function (array $event) use (&$commands): void { $this->fail(sprintf('"%s" command was executed', $event['started']->getCommandName())); } ); diff --git a/tests/Model/CollectionInfoTest.php b/tests/Model/CollectionInfoTest.php index 4320e7df1..ff5a112db 100644 --- a/tests/Model/CollectionInfoTest.php +++ b/tests/Model/CollectionInfoTest.php @@ -8,22 +8,50 @@ class CollectionInfoTest extends TestCase { - public function testGetName() + public function testGetBasicInformation(): void { - $info = new CollectionInfo(['name' => 'foo']); + $info = new CollectionInfo([ + 'name' => 'foo', + 'type' => 'view', + 'options' => ['capped' => true, 'size' => 1048576], + 'info' => ['readOnly' => true], + 'idIndex' => ['idIndex' => true], // Dummy option + ]); + $this->assertSame('foo', $info->getName()); + $this->assertSame('foo', $info['name']); + + $this->assertSame('view', $info->getType()); + $this->assertSame('view', $info['type']); + + $this->assertSame(['capped' => true, 'size' => 1048576], $info->getOptions()); + $this->assertSame(['capped' => true, 'size' => 1048576], $info['options']); + + $this->assertSame(['readOnly' => true], $info->getInfo()); + $this->assertSame(['readOnly' => true], $info['info']); + + $this->assertSame(['idIndex' => true], $info->getIdIndex()); + $this->assertSame(['idIndex' => true], $info['idIndex']); } - public function testGetOptions() + public function testMissingFields(): void { - $info = new CollectionInfo(['name' => 'foo']); + $info = new CollectionInfo([ + 'name' => 'foo', + 'type' => 'view', + ]); + $this->assertSame([], $info->getOptions()); + $this->assertArrayNotHasKey('options', $info); - $info = new CollectionInfo(['name' => 'foo', 'options' => ['capped' => true, 'size' => 1048576]]); - $this->assertSame(['capped' => true, 'size' => 1048576], $info->getOptions()); + $this->assertSame([], $info->getInfo()); + $this->assertArrayNotHasKey('info', $info); + + $this->assertSame([], $info->getIdIndex()); + $this->assertArrayNotHasKey('idIndex', $info); } - public function testCappedCollectionMethods() + public function testCappedCollectionMethods(): void { $info = new CollectionInfo(['name' => 'foo']); $this->assertFalse($info->isCapped()); @@ -41,7 +69,7 @@ public function testCappedCollectionMethods() $this->assertSame(1048576, $info->getCappedSize()); } - public function testDebugInfo() + public function testDebugInfo(): void { $expectedInfo = [ 'name' => 'foo', @@ -52,7 +80,7 @@ public function testDebugInfo() $this->assertSame($expectedInfo, $info->__debugInfo()); } - public function testImplementsArrayAccess() + public function testImplementsArrayAccess(): void { $info = new CollectionInfo(['name' => 'foo']); $this->assertInstanceOf('ArrayAccess', $info); @@ -60,7 +88,7 @@ public function testImplementsArrayAccess() $this->assertSame('foo', $info['name']); } - public function testOffsetSetCannotBeCalled() + public function testOffsetSetCannotBeCalled(): void { $info = new CollectionInfo(['name' => 'foo', 'options' => ['capped' => true, 'size' => 1048576]]); $this->expectException(BadMethodCallException::class); @@ -68,7 +96,7 @@ public function testOffsetSetCannotBeCalled() $info['options'] = ['capped' => false]; } - public function testOffsetUnsetCannotBeCalled() + public function testOffsetUnsetCannotBeCalled(): void { $info = new CollectionInfo(['name' => 'foo', 'options' => ['capped' => true, 'size' => 1048576]]); $this->expectException(BadMethodCallException::class); diff --git a/tests/Model/DatabaseInfoTest.php b/tests/Model/DatabaseInfoTest.php index 2c9819ee6..14e083a08 100644 --- a/tests/Model/DatabaseInfoTest.php +++ b/tests/Model/DatabaseInfoTest.php @@ -8,19 +8,19 @@ class DatabaseInfoTest extends TestCase { - public function testGetName() + public function testGetName(): void { $info = new DatabaseInfo(['name' => 'foo']); $this->assertSame('foo', $info->getName()); } - public function testGetSizeOnDisk() + public function testGetSizeOnDisk(): void { $info = new DatabaseInfo(['sizeOnDisk' => 1048576]); $this->assertSame(1048576, $info->getSizeOnDisk()); } - public function testIsEmpty() + public function testIsEmpty(): void { $info = new DatabaseInfo(['empty' => false]); $this->assertFalse($info->isEmpty()); @@ -29,7 +29,7 @@ public function testIsEmpty() $this->assertTrue($info->isEmpty()); } - public function testDebugInfo() + public function testDebugInfo(): void { $expectedInfo = [ 'name' => 'foo', @@ -41,7 +41,7 @@ public function testDebugInfo() $this->assertSame($expectedInfo, $info->__debugInfo()); } - public function testImplementsArrayAccess() + public function testImplementsArrayAccess(): void { $info = new DatabaseInfo(['name' => 'foo']); $this->assertInstanceOf('ArrayAccess', $info); @@ -49,7 +49,7 @@ public function testImplementsArrayAccess() $this->assertSame('foo', $info['name']); } - public function testOffsetSetCannotBeCalled() + public function testOffsetSetCannotBeCalled(): void { $info = new DatabaseInfo(['name' => 'foo', 'sizeOnDisk' => 1048576, 'empty' => false]); $this->expectException(BadMethodCallException::class); @@ -57,7 +57,7 @@ public function testOffsetSetCannotBeCalled() $info['empty'] = true; } - public function testOffsetUnsetCannotBeCalled() + public function testOffsetUnsetCannotBeCalled(): void { $info = new DatabaseInfo(['name' => 'foo', 'sizeOnDisk' => 1048576, 'empty' => false]); $this->expectException(BadMethodCallException::class); diff --git a/tests/Model/IndexInfoFunctionalTest.php b/tests/Model/IndexInfoFunctionalTest.php index d97a3ca23..a45a97724 100644 --- a/tests/Model/IndexInfoFunctionalTest.php +++ b/tests/Model/IndexInfoFunctionalTest.php @@ -4,36 +4,20 @@ use MongoDB\Collection; use MongoDB\Tests\FunctionalTestCase; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; -use function version_compare; class IndexInfoFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; - /** @var Collection */ private $collection; - private function doSetUp() + public function setUp(): void { parent::setUp(); - $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); - $this->collection->drop(); + $this->collection = $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); } - private function doTearDown() - { - if ($this->hasFailed()) { - return; - } - - $this->collection->drop(); - - parent::tearDown(); - } - - public function testIs2dSphere() + public function testIs2dSphere(): void { $indexName = $this->collection->createIndex(['pos' => '2dsphere']); $result = $this->collection->listIndexes(); @@ -45,12 +29,19 @@ public function testIs2dSphere() $this->assertEquals($indexName, $index->getName()); $this->assertTrue($index->is2dSphere()); - $expectedVersion = version_compare($this->getServerVersion(), '3.2.0', '<') ? 2 : 3; - $this->assertEquals($expectedVersion, $index['2dsphereIndexVersion']); + // MongoDB 3.2+ reports index version 3 + $this->assertEquals(3, $index['2dsphereIndexVersion']); } - public function testIsGeoHaystack() + /** + * @group matrix-testing-exclude-server-5.0-driver-4.0 + * @group matrix-testing-exclude-server-5.0-driver-4.2 + * @group matrix-testing-exclude-server-5.0-driver-4.4 + */ + public function testIsGeoHaystack(): void { + $this->skipIfGeoHaystackIndexIsNotSupported(); + $indexName = $this->collection->createIndex(['pos' => 'geoHaystack', 'x' => 1], ['bucketSize' => 5]); $result = $this->collection->listIndexes(); @@ -59,11 +50,13 @@ public function testIsGeoHaystack() $index = $result->current(); $this->assertEquals($indexName, $index->getName()); - $this->assertTrue($index->isGeoHaystack()); + $this->assertDeprecated(function () use ($index): void { + $this->assertTrue($index->isGeoHaystack()); + }); $this->assertEquals(5, $index['bucketSize']); } - public function testIsText() + public function testIsText(): void { $indexName = $this->collection->createIndex(['x' => 'text']); $result = $this->collection->listIndexes(); @@ -77,8 +70,8 @@ public function testIsText() $this->assertEquals('english', $index['default_language']); $this->assertEquals('language', $index['language_override']); - $expectedVersion = version_compare($this->getServerVersion(), '3.2.0', '<') ? 2 : 3; - $this->assertEquals($expectedVersion, $index['textIndexVersion']); + // MongoDB 3.2+ reports index version 3 + $this->assertEquals(3, $index['textIndexVersion']); $this->assertSameDocument(['x' => 1], $index['weights']); } diff --git a/tests/Model/IndexInfoTest.php b/tests/Model/IndexInfoTest.php index 33cfcda52..30cb26aea 100644 --- a/tests/Model/IndexInfoTest.php +++ b/tests/Model/IndexInfoTest.php @@ -8,7 +8,7 @@ class IndexInfoTest extends TestCase { - public function testBasicIndex() + public function testBasicIndex(): void { $info = new IndexInfo([ 'v' => 1, @@ -22,14 +22,16 @@ public function testBasicIndex() $this->assertSame('x_1', $info->getName()); $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); + $this->assertDeprecated(function () use ($info): void { + $this->assertFalse($info->isGeoHaystack()); + }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); $this->assertFalse($info->isUnique()); } - public function testSparseIndex() + public function testSparseIndex(): void { $info = new IndexInfo([ 'v' => 1, @@ -44,14 +46,16 @@ public function testSparseIndex() $this->assertSame('y_sparse', $info->getName()); $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); + $this->assertDeprecated(function () use ($info): void { + $this->assertFalse($info->isGeoHaystack()); + }); $this->assertTrue($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); $this->assertFalse($info->isUnique()); } - public function testUniqueIndex() + public function testUniqueIndex(): void { $info = new IndexInfo([ 'v' => 1, @@ -66,14 +70,16 @@ public function testUniqueIndex() $this->assertSame('z_unique', $info->getName()); $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); + $this->assertDeprecated(function () use ($info): void { + $this->assertFalse($info->isGeoHaystack()); + }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); $this->assertTrue($info->isUnique()); } - public function testTtlIndex() + public function testTtlIndex(): void { $info = new IndexInfo([ 'v' => 1, @@ -88,7 +94,9 @@ public function testTtlIndex() $this->assertSame('z_unique', $info->getName()); $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); + $this->assertDeprecated(function () use ($info): void { + $this->assertFalse($info->isGeoHaystack()); + }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertTrue($info->isTtl()); @@ -97,7 +105,7 @@ public function testTtlIndex() $this->assertSame(100, $info['expireAfterSeconds']); } - public function testDebugInfo() + public function testDebugInfo(): void { $expectedInfo = [ 'v' => 1, @@ -110,7 +118,7 @@ public function testDebugInfo() $this->assertSame($expectedInfo, $info->__debugInfo()); } - public function testImplementsArrayAccess() + public function testImplementsArrayAccess(): void { $info = new IndexInfo([ 'v' => 1, @@ -124,7 +132,7 @@ public function testImplementsArrayAccess() $this->assertSame('x_1', $info['name']); } - public function testOffsetSetCannotBeCalled() + public function testOffsetSetCannotBeCalled(): void { $info = new IndexInfo([ 'v' => 1, @@ -138,7 +146,7 @@ public function testOffsetSetCannotBeCalled() $info['v'] = 2; } - public function testOffsetUnsetCannotBeCalled() + public function testOffsetUnsetCannotBeCalled(): void { $info = new IndexInfo([ 'v' => 1, @@ -152,7 +160,7 @@ public function testOffsetUnsetCannotBeCalled() unset($info['v']); } - public function testIs2dSphere() + public function testIs2dSphere(): void { $info = new IndexInfo([ 'v' => 2, @@ -166,14 +174,16 @@ public function testIs2dSphere() $this->assertSame('pos_2dsphere', $info->getName()); $this->assertSame('foo.bar', $info->getNamespace()); $this->assertTrue($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); + $this->assertDeprecated(function () use ($info): void { + $this->assertFalse($info->isGeoHaystack()); + }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); $this->assertFalse($info->isUnique()); } - public function testIsGeoHaystack() + public function testIsGeoHaystack(): void { $info = new IndexInfo([ 'v' => 2, @@ -187,14 +197,16 @@ public function testIsGeoHaystack() $this->assertSame('pos2_geoHaystack_x_1', $info->getName()); $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertTrue($info->isGeoHaystack()); + $this->assertDeprecated(function () use ($info): void { + $this->assertTrue($info->isGeoHaystack()); + }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); $this->assertFalse($info->isUnique()); } - public function testIsText() + public function testIsText(): void { $info = new IndexInfo([ 'v' => 2, @@ -208,7 +220,9 @@ public function testIsText() $this->assertSame('title_text_description_text', $info->getName()); $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); + $this->assertDeprecated(function () use ($info): void { + $this->assertFalse($info->isGeoHaystack()); + }); $this->assertFalse($info->isSparse()); $this->assertTrue($info->isText()); $this->assertFalse($info->isTtl()); diff --git a/tests/Model/IndexInputTest.php b/tests/Model/IndexInputTest.php index da6876169..b9653afda 100644 --- a/tests/Model/IndexInputTest.php +++ b/tests/Model/IndexInputTest.php @@ -2,30 +2,30 @@ namespace MongoDB\Tests\Model; +use MongoDB\BSON\Document; use MongoDB\BSON\Serializable; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Model\BSONDocument; use MongoDB\Model\IndexInput; use MongoDB\Tests\TestCase; use stdClass; class IndexInputTest extends TestCase { - public function testConstructorShouldRequireKey() + public function testConstructorShouldRequireKey(): void { $this->expectException(InvalidArgumentException::class); new IndexInput([]); } - public function testConstructorShouldRequireKeyToBeArrayOrObject() + public function testConstructorShouldRequireKeyToBeArrayOrObject(): void { $this->expectException(InvalidArgumentException::class); new IndexInput(['key' => 'foo']); } - /** - * @dataProvider provideInvalidFieldOrderValues - */ - public function testConstructorShouldRequireKeyFieldOrderToBeNumericOrString($order) + /** @dataProvider provideInvalidFieldOrderValues */ + public function testConstructorShouldRequireKeyFieldOrderToBeNumericOrString($order): void { $this->expectException(InvalidArgumentException::class); new IndexInput(['key' => ['x' => $order]]); @@ -36,7 +36,7 @@ public function provideInvalidFieldOrderValues() return $this->wrapValuesForDataProvider([true, [], new stdClass()]); } - public function testConstructorShouldRequireNameToBeString() + public function testConstructorShouldRequireNameToBeString(): void { $this->expectException(InvalidArgumentException::class); new IndexInput(['key' => ['x' => 1], 'name' => 1]); @@ -44,16 +44,20 @@ public function testConstructorShouldRequireNameToBeString() /** * @dataProvider provideExpectedNameAndKey + * @param array|object $key */ - public function testNameGeneration($expectedName, array $key) + public function testNameGeneration($expectedName, $key): void { $this->assertSame($expectedName, (string) new IndexInput(['key' => $key])); } - public function provideExpectedNameAndKey() + public function provideExpectedNameAndKey(): array { return [ ['x_1', ['x' => 1]], + ['x_1', (object) ['x' => 1]], + ['x_1', new BSONDocument(['x' => 1])], + ['x_1', Document::fromPHP(['x' => 1])], ['x_1_y_-1', ['x' => 1, 'y' => -1]], ['loc_2dsphere', ['loc' => '2dsphere']], ['loc_2dsphere_x_1', ['loc' => '2dsphere', 'x' => 1]], @@ -61,7 +65,7 @@ public function provideExpectedNameAndKey() ]; } - public function testBsonSerialization() + public function testBsonSerialization(): void { $expected = [ 'key' => ['x' => 1], diff --git a/tests/Operation/AggregateFunctionalTest.php b/tests/Operation/AggregateFunctionalTest.php index 64e53f1b7..4e7582975 100644 --- a/tests/Operation/AggregateFunctionalTest.php +++ b/tests/Operation/AggregateFunctionalTest.php @@ -2,7 +2,6 @@ namespace MongoDB\Tests\Operation; -use ArrayIterator; use MongoDB\Collection; use MongoDB\Driver\BulkWrite; use MongoDB\Driver\Exception\RuntimeException; @@ -11,16 +10,34 @@ use MongoDB\Operation\Aggregate; use MongoDB\Tests\CommandObserver; use stdClass; + use function current; use function iterator_to_array; -use function version_compare; class AggregateFunctionalTest extends FunctionalTestCase { - public function testBatchSizeIsIgnoredIfPipelineIncludesOutStage() + public function testAllowDiskUseIsOmittedByDefault(): void { (new CommandObserver())->observe( - function () { + function (): void { + $operation = new Aggregate( + $this->getDatabaseName(), + $this->getCollectionName(), + [['$match' => ['x' => 1]]] + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event): void { + $this->assertObjectNotHasAttribute('allowDiskUse', $event['started']->getCommand()); + } + ); + } + + public function testBatchSizeIsIgnoredIfPipelineIncludesOutStage(): void + { + (new CommandObserver())->observe( + function (): void { $operation = new Aggregate( $this->getDatabaseName(), $this->getCollectionName(), @@ -30,7 +47,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertEquals(new stdClass(), $event['started']->getCommand()->cursor); } ); @@ -39,14 +56,10 @@ function (array $event) { $outCollection->drop(); } - public function testCurrentOpCommand() + public function testCurrentOpCommand(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('$currentOp is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new Aggregate( 'admin', null, @@ -55,16 +68,16 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertSame(1, $event['started']->getCommand()->aggregate); } ); } - public function testDefaultReadConcernIsOmitted() + public function testDefaultReadConcernIsOmitted(): void { (new CommandObserver())->observe( - function () { + function (): void { $operation = new Aggregate( $this->getDatabaseName(), $this->getCollectionName(), @@ -74,16 +87,16 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); } ); } - public function testDefaultWriteConcernIsOmitted() + public function testDefaultWriteConcernIsOmitted(): void { (new CommandObserver())->observe( - function () { + function (): void { $operation = new Aggregate( $this->getDatabaseName(), $this->getCollectionName(), @@ -93,7 +106,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); } ); @@ -102,7 +115,7 @@ function (array $event) { $outCollection->drop(); } - public function testEmptyPipelineReturnsAllDocuments() + public function testEmptyPipelineReturnsAllDocuments(): void { $this->createFixtures(3); @@ -118,21 +131,17 @@ public function testEmptyPipelineReturnsAllDocuments() $this->assertEquals($expectedDocuments, $results); } - public function testUnrecognizedPipelineState() + public function testUnrecognizedPipelineState(): void { $operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), [['$foo' => 1]]); $this->expectException(RuntimeException::class); $operation->execute($this->getPrimaryServer()); } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new Aggregate( $this->getDatabaseName(), $this->getCollectionName(), @@ -142,16 +151,14 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ - public function testTypeMapOption(array $typeMap = null, array $expectedDocuments) + /** @dataProvider provideTypeMapOptionsAndExpectedDocuments */ + public function testTypeMapOption(?array $typeMap, array $expectedDocuments): void { $this->createFixtures(3); @@ -163,27 +170,7 @@ public function testTypeMapOption(array $typeMap = null, array $expectedDocument $this->assertEquals($expectedDocuments, $results); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ - public function testTypeMapOptionWithoutCursor(array $typeMap = null, array $expectedDocuments) - { - if (version_compare($this->getServerVersion(), '3.6.0', '>=')) { - $this->markTestSkipped('Aggregations with useCursor == false are not supported'); - } - - $this->createFixtures(3); - - $pipeline = [['$match' => ['_id' => ['$ne' => 2]]]]; - - $operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), $pipeline, ['typeMap' => $typeMap, 'useCursor' => false]); - $results = $operation->execute($this->getPrimaryServer()); - - $this->assertInstanceOf(ArrayIterator::class, $results); - $this->assertEquals($expectedDocuments, iterator_to_array($results)); - } - - public function testExplainOption() + public function testExplainOption(): void { $this->createFixtures(3); @@ -201,19 +188,15 @@ public function testExplainOption() )); } - public function testExplainOptionWithWriteConcern() + public function testExplainOptionWithWriteConcern(): void { - if (version_compare($this->getServerVersion(), '3.4.0', '<')) { - $this->markTestSkipped('The writeConcern option is not supported'); - } - $this->createFixtures(3); $pipeline = [['$match' => ['_id' => ['$ne' => 2]]], ['$out' => $this->getCollectionName() . '.output']]; $options = ['explain' => true, 'writeConcern' => new WriteConcern(1)]; (new CommandObserver())->observe( - function () use ($pipeline, $options) { + function () use ($pipeline, $options): void { $operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), $pipeline, $options); $results = iterator_to_array($operation->execute($this->getPrimaryServer())); @@ -229,7 +212,7 @@ function () use ($pipeline, $options) { $this->assertObjectHasAttribute('stages', $result); } }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); } ); @@ -237,14 +220,10 @@ function (array $event) { $this->assertCollectionCount($this->getCollectionName() . '.output', 0); } - public function testBypassDocumentValidationSetWhenTrue() + public function testBypassDocumentValidationSetWhenTrue(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new Aggregate( $this->getDatabaseName(), $this->getCollectionName(), @@ -254,21 +233,17 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); } ); } - public function testBypassDocumentValidationUnsetWhenFalse() + public function testBypassDocumentValidationUnsetWhenFalse(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new Aggregate( $this->getDatabaseName(), $this->getCollectionName(), @@ -278,7 +253,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); } ); @@ -325,12 +300,12 @@ public function provideTypeMapOptionsAndExpectedDocuments() ]; } - public function testReadPreferenceWithinTransaction() + public function testReadPreferenceWithinTransaction(): void { $this->skipIfTransactionsAreNotSupported(); // Collection must be created before the transaction starts - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -340,7 +315,7 @@ public function testReadPreferenceWithinTransaction() $pipeline = [['$match' => ['_id' => ['$lt' => 3]]]]; $options = [ - 'readPreference' => new ReadPreference('primary'), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), 'session' => $session, ]; @@ -362,11 +337,8 @@ public function testReadPreferenceWithinTransaction() /** * Create data fixtures. - * - * @param integer $n - * @param array $executeBulkWriteOptions */ - private function createFixtures($n, array $executeBulkWriteOptions = []) + private function createFixtures(int $n, array $executeBulkWriteOptions = []): void { $bulkWrite = new BulkWrite(['ordered' => true]); diff --git a/tests/Operation/AggregateTest.php b/tests/Operation/AggregateTest.php index d8898b321..f72fc5a22 100644 --- a/tests/Operation/AggregateTest.php +++ b/tests/Operation/AggregateTest.php @@ -2,22 +2,23 @@ namespace MongoDB\Tests\Operation; +use MongoDB\Driver\ReadConcern; +use MongoDB\Driver\ReadPreference; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Aggregate; class AggregateTest extends TestCase { - public function testConstructorPipelineArgumentMustBeAList() + public function testConstructorPipelineArgumentMustBeAList(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$pipeline is not a list (unexpected index: "1")'); + $this->expectExceptionMessage('$pipeline is not a valid aggregation pipeline'); new Aggregate($this->getDatabaseName(), $this->getCollectionName(), [1 => ['$match' => ['x' => 1]]]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Aggregate($this->getDatabaseName(), $this->getCollectionName(), [['$match' => ['x' => 1]]], $options); @@ -27,7 +28,7 @@ public function provideInvalidConstructorOptions() { $options = []; - foreach ($this->getInvalidBooleanValues(true) as $value) { + foreach ($this->getInvalidBooleanValues() as $value) { $options[][] = ['allowDiskUse' => $value]; } @@ -43,14 +44,14 @@ public function provideInvalidConstructorOptions() $options[][] = ['collation' => $value]; } - foreach ($this->getInvalidStringValues() as $value) { - $options[][] = ['comment' => $value]; - } - foreach ($this->getInvalidHintValues() as $value) { $options[][] = ['hint' => $value]; } + foreach ($this->getInvalidDocumentValues() as $value) { + $options[][] = ['let' => $value]; + } + foreach ($this->getInvalidBooleanValues() as $value) { $options[][] = ['explain' => $value]; } @@ -90,7 +91,7 @@ public function provideInvalidConstructorOptions() return $options; } - public function testConstructorBatchSizeOptionRequiresUseCursor() + public function testConstructorBatchSizeOptionRequiresUseCursor(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('"batchSize" option should not be used if "useCursor" is false'); @@ -106,4 +107,41 @@ private function getInvalidHintValues() { return [123, 3.14, true]; } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'allowDiskUse' => true, + 'batchSize' => 100, + 'bypassDocumentValidation' => true, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'let' => ['a' => 1], + 'maxTimeMS' => 100, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + 'useCursor' => true, + // Intentionally omitted options + // The "explain" option is illegal + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), + 'typeMap' => ['root' => 'array', 'document' => 'array'], + 'writeConcern' => new WriteConcern(0), + ]; + $operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), [['$project' => ['_id' => 0]]], $options); + + $expected = [ + 'aggregate' => $this->getCollectionName(), + 'pipeline' => [['$project' => ['_id' => 0]]], + 'allowDiskUse' => true, + 'bypassDocumentValidation' => true, + 'collation' => (object) ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + 'let' => (object) ['a' => 1], + 'cursor' => ['batchSize' => 100], + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); + } } diff --git a/tests/Operation/BulkWriteFunctionalTest.php b/tests/Operation/BulkWriteFunctionalTest.php index d2de54d5e..2acb747bf 100644 --- a/tests/Operation/BulkWriteFunctionalTest.php +++ b/tests/Operation/BulkWriteFunctionalTest.php @@ -2,6 +2,7 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; use MongoDB\BSON\ObjectId; use MongoDB\BulkWriteResult; use MongoDB\Collection; @@ -11,24 +12,23 @@ use MongoDB\Model\BSONDocument; use MongoDB\Operation\BulkWrite; use MongoDB\Tests\CommandObserver; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; -use function version_compare; +use stdClass; + +use function is_array; class BulkWriteFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; - /** @var Collection */ private $collection; - private function doSetUp() + public function setUp(): void { parent::setUp(); $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); } - public function testInserts() + public function testInserts(): void { $ops = [ ['insertOne' => [['_id' => 1, 'x' => 11]]], @@ -59,7 +59,61 @@ public function testInserts() $this->assertSameDocuments($expected, $this->collection->find()); } - public function testUpdates() + /** + * @dataProvider provideDocumentsWithIds + * @dataProvider provideDocumentsWithoutIds + */ + public function testInsertDocumentEncoding($document, stdClass $expectedDocument): void + { + (new CommandObserver())->observe( + function () use ($document, $expectedDocument): void { + $operation = new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [['insertOne' => [$document]]] + ); + + $result = $operation->execute($this->getPrimaryServer()); + + // Replace _id placeholder if necessary + if ($expectedDocument->_id === null) { + $expectedDocument->_id = $result->getInsertedIds()[0]; + } + }, + function (array $event) use ($expectedDocument): void { + $this->assertEquals($expectedDocument, $event['started']->getCommand()->documents[0] ?? null); + } + ); + } + + public function provideDocumentsWithIds(): array + { + $expectedDocument = (object) ['_id' => 1]; + + return [ + 'with_id:array' => [['_id' => 1], $expectedDocument], + 'with_id:object' => [(object) ['_id' => 1], $expectedDocument], + 'with_id:Serializable' => [new BSONDocument(['_id' => 1]), $expectedDocument], + 'with_id:Document' => [Document::fromPHP(['_id' => 1]), $expectedDocument], + ]; + } + + public function provideDocumentsWithoutIds(): array + { + /* Note: _id placeholders must be replaced with generated ObjectIds. We + * also clone the value for each data set since tests may need to modify + * the object. */ + $expectedDocument = (object) ['_id' => null, 'x' => 1]; + + return [ + 'without_id:array' => [['x' => 1], clone $expectedDocument], + 'without_id:object' => [(object) ['x' => 1], clone $expectedDocument], + 'without_id:Serializable' => [new BSONDocument(['x' => 1]), clone $expectedDocument], + 'without_id:Document' => [Document::fromPHP(['x' => 1]), clone $expectedDocument], + ]; + } + + public function testUpdates(): void { $this->createFixtures(4); @@ -95,7 +149,81 @@ public function testUpdates() $this->assertSameDocuments($expected, $this->collection->find()); } - public function testDeletes() + /** @dataProvider provideFilterDocuments */ + public function testUpdateFilterDocuments($filter, stdClass $expectedFilter): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [ + ['replaceOne' => [$filter, ['x' => 1]]], + ['updateOne' => [$filter, ['$set' => ['x' => 1]]]], + ['updateMany' => [$filter, ['$set' => ['x' => 1]]]], + ] + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedFilter): void { + $this->assertEquals($expectedFilter, $event['started']->getCommand()->updates[0]->q ?? null); + $this->assertEquals($expectedFilter, $event['started']->getCommand()->updates[1]->q ?? null); + $this->assertEquals($expectedFilter, $event['started']->getCommand()->updates[2]->q ?? null); + } + ); + } + + /** @dataProvider provideReplacementDocuments */ + public function testReplacementDocuments($replacement, stdClass $expectedReplacement): void + { + (new CommandObserver())->observe( + function () use ($replacement): void { + $operation = new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [['replaceOne' => [['x' => 1], $replacement]]] + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedReplacement): void { + $this->assertEquals($expectedReplacement, $event['started']->getCommand()->updates[0]->u ?? null); + } + ); + } + + /** + * @dataProvider provideUpdateDocuments + * @dataProvider provideUpdatePipelines + */ + public function testUpdateDocuments($update, $expectedUpdate): void + { + if (is_array($expectedUpdate)) { + $this->skipIfServerVersion('<', '4.2.0', 'Pipeline-style updates are not supported'); + } + + (new CommandObserver())->observe( + function () use ($update): void { + $operation = new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [ + ['updateOne' => [['x' => 1], $update]], + ['updateMany' => [['x' => 1], $update]], + ] + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedUpdate): void { + $this->assertEquals($expectedUpdate, $event['started']->getCommand()->updates[0]->u ?? null); + $this->assertEquals($expectedUpdate, $event['started']->getCommand()->updates[1]->u ?? null); + } + ); + } + + public function testDeletes(): void { $this->createFixtures(4); @@ -117,7 +245,30 @@ public function testDeletes() $this->assertSameDocuments($expected, $this->collection->find()); } - public function testMixedOrderedOperations() + /** @dataProvider provideFilterDocuments */ + public function testDeleteFilterDocuments($filter, stdClass $expectedQuery): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [ + ['deleteOne' => [$filter]], + ['deleteMany' => [$filter]], + ] + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->deletes[0]->q ?? null); + $this->assertEquals($expectedQuery, $event['started']->getCommand()->deletes[1]->q ?? null); + } + ); + } + + public function testMixedOrderedOperations(): void { $this->createFixtures(3); @@ -165,74 +316,58 @@ public function testUnacknowledgedWriteConcern() return $result; } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesDeletedCount(BulkWriteResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesDeletedCount(BulkWriteResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); $result->getDeletedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesInsertCount(BulkWriteResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesInsertCount(BulkWriteResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); $result->getInsertedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesMatchedCount(BulkWriteResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesMatchedCount(BulkWriteResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); $result->getMatchedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesModifiedCount(BulkWriteResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesModifiedCount(BulkWriteResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); $result->getModifiedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesUpsertedCount(BulkWriteResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesUpsertedCount(BulkWriteResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); $result->getUpsertedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesUpsertedIds(BulkWriteResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesUpsertedIds(BulkWriteResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); $result->getUpsertedIds(); } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new BulkWrite( $this->getDatabaseName(), $this->getCollectionName(), @@ -242,20 +377,16 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); } - public function testBypassDocumentValidationSetWhenTrue() + public function testBypassDocumentValidationSetWhenTrue(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new BulkWrite( $this->getDatabaseName(), $this->getCollectionName(), @@ -265,21 +396,17 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); } ); } - public function testBypassDocumentValidationUnsetWhenFalse() + public function testBypassDocumentValidationUnsetWhenFalse(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new BulkWrite( $this->getDatabaseName(), $this->getCollectionName(), @@ -289,17 +416,15 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); } ); } - public function testBulkWriteWithPipelineUpdates() + public function testBulkWriteWithPipelineUpdates(): void { - if (version_compare($this->getServerVersion(), '4.2.0', '<')) { - $this->markTestSkipped('Pipeline-style updates are not supported'); - } + $this->skipIfServerVersion('<', '4.2.0', 'Pipeline-style updates are not supported'); $this->createFixtures(4); @@ -327,10 +452,8 @@ public function testBulkWriteWithPipelineUpdates() /** * Create data fixtures. - * - * @param integer $n */ - private function createFixtures($n) + private function createFixtures(int $n): void { $bulkWrite = new Bulk(['ordered' => true]); diff --git a/tests/Operation/BulkWriteTest.php b/tests/Operation/BulkWriteTest.php index 7beb01f4d..444178cf7 100644 --- a/tests/Operation/BulkWriteTest.php +++ b/tests/Operation/BulkWriteTest.php @@ -7,23 +7,23 @@ class BulkWriteTest extends TestCase { - public function testOperationsMustNotBeEmpty() + public function testOperationsMustNotBeEmpty(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$operations is empty'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), []); } - public function testOperationsMustBeAList() + public function testOperationsMustBeAList(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$operations is not a list (unexpected index: "1")'); + $this->expectExceptionMessage('$operations is not a list'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ 1 => [BulkWrite::INSERT_ONE => [['x' => 1]]], ]); } - public function testMultipleOperationsInOneElement() + public function testMultipleOperationsInOneElement(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Expected one element in $operation[0], actually: 2'); @@ -35,7 +35,7 @@ public function testMultipleOperationsInOneElement() ]); } - public function testUnknownOperation() + public function testUnknownOperation(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Unknown operation type "foo" in $operations[0]'); @@ -44,7 +44,7 @@ public function testUnknownOperation() ]); } - public function testInsertOneDocumentArgumentMissing() + public function testInsertOneDocumentArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Missing first argument for $operations[0]["insertOne"]'); @@ -53,10 +53,8 @@ public function testInsertOneDocumentArgumentMissing() ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testInsertOneDocumentArgumentTypeCheck($document) + /** @dataProvider provideInvalidDocumentValues */ + public function testInsertOneDocumentArgumentTypeCheck($document): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["insertOne"\]\[0\] to have type "array or object" but found "[\w ]+"/'); @@ -65,7 +63,7 @@ public function testInsertOneDocumentArgumentTypeCheck($document) ]); } - public function testDeleteManyFilterArgumentMissing() + public function testDeleteManyFilterArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Missing first argument for $operations[0]["deleteMany"]'); @@ -74,10 +72,8 @@ public function testDeleteManyFilterArgumentMissing() ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testDeleteManyFilterArgumentTypeCheck($document) + /** @dataProvider provideInvalidDocumentValues */ + public function testDeleteManyFilterArgumentTypeCheck($document): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteMany"\]\[0\] to have type "array or object" but found "[\w ]+"/'); @@ -86,10 +82,8 @@ public function testDeleteManyFilterArgumentTypeCheck($document) ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testDeleteManyCollationOptionTypeCheck($collation) + /** @dataProvider provideInvalidDocumentValues */ + public function testDeleteManyCollationOptionTypeCheck($collation): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteMany"\]\[1\]\["collation"\] to have type "array or object" but found "[\w ]+"/'); @@ -103,7 +97,7 @@ public function provideInvalidDocumentValues() return $this->wrapValuesForDataProvider($this->getInvalidDocumentValues()); } - public function testDeleteOneFilterArgumentMissing() + public function testDeleteOneFilterArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Missing first argument for $operations[0]["deleteOne"]'); @@ -112,10 +106,8 @@ public function testDeleteOneFilterArgumentMissing() ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testDeleteOneFilterArgumentTypeCheck($document) + /** @dataProvider provideInvalidDocumentValues */ + public function testDeleteOneFilterArgumentTypeCheck($document): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteOne"\]\[0\] to have type "array or object" but found "[\w ]+"/'); @@ -124,10 +116,8 @@ public function testDeleteOneFilterArgumentTypeCheck($document) ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testDeleteOneCollationOptionTypeCheck($collation) + /** @dataProvider provideInvalidDocumentValues */ + public function testDeleteOneCollationOptionTypeCheck($collation): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteOne"\]\[1\]\["collation"\] to have type "array or object" but found "[\w ]+"/'); @@ -136,7 +126,7 @@ public function testDeleteOneCollationOptionTypeCheck($collation) ]); } - public function testReplaceOneFilterArgumentMissing() + public function testReplaceOneFilterArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Missing first argument for $operations[0]["replaceOne"]'); @@ -145,10 +135,8 @@ public function testReplaceOneFilterArgumentMissing() ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testReplaceOneFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testReplaceOneFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[0\] to have type "array or object" but found "[\w ]+"/'); @@ -157,7 +145,18 @@ public function testReplaceOneFilterArgumentTypeCheck($filter) ]); } - public function testReplaceOneReplacementArgumentMissing() + /** + * @dataProvider provideReplacementDocuments + * @doesNotPerformAssertions + */ + public function testReplaceOneReplacementArgument($replacement): void + { + new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ + [BulkWrite::REPLACE_ONE => [['x' => 1], $replacement]], + ]); + } + + public function testReplaceOneReplacementArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Missing second argument for $operations[0]["replaceOne"]'); @@ -166,10 +165,8 @@ public function testReplaceOneReplacementArgumentMissing() ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testReplaceOneReplacementArgumentTypeCheck($replacement) + /** @dataProvider provideInvalidDocumentValues */ + public function testReplaceOneReplacementArgumentTypeCheck($replacement): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[1\] to have type "array or object" but found "[\w ]+"/'); @@ -178,19 +175,31 @@ public function testReplaceOneReplacementArgumentTypeCheck($replacement) ]); } - public function testReplaceOneReplacementArgumentRequiresNoOperators() + /** @dataProvider provideUpdateDocuments */ + public function testReplaceOneReplacementArgumentProhibitsUpdateDocument($replacement): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('First key in $operations[0]["replaceOne"][1] is an update operator'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ - [BulkWrite::REPLACE_ONE => [['_id' => 1], ['$inc' => ['x' => 1]]]], + [BulkWrite::REPLACE_ONE => [['x' => 1], $replacement]], ]); } /** - * @dataProvider provideInvalidDocumentValues + * @dataProvider provideUpdatePipelines + * @dataProvider provideEmptyUpdatePipelinesExcludingArray */ - public function testReplaceOneCollationOptionTypeCheck($collation) + public function testReplaceOneReplacementArgumentProhibitsUpdatePipeline($replacement): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$operations[0]["replaceOne"][1] is an update pipeline'); + new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ + [BulkWrite::REPLACE_ONE => [['x' => 1], $replacement]], + ]); + } + + /** @dataProvider provideInvalidDocumentValues */ + public function testReplaceOneCollationOptionTypeCheck($collation): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/'); @@ -199,10 +208,8 @@ public function testReplaceOneCollationOptionTypeCheck($collation) ]); } - /** - * @dataProvider provideInvalidBooleanValues - */ - public function testReplaceOneUpsertOptionTypeCheck($upsert) + /** @dataProvider provideInvalidBooleanValues */ + public function testReplaceOneUpsertOptionTypeCheck($upsert): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/'); @@ -216,7 +223,7 @@ public function provideInvalidBooleanValues() return $this->wrapValuesForDataProvider($this->getInvalidBooleanValues()); } - public function testUpdateManyFilterArgumentMissing() + public function testUpdateManyFilterArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Missing first argument for $operations[0]["updateMany"]'); @@ -225,10 +232,8 @@ public function testUpdateManyFilterArgumentMissing() ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testUpdateManyFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testUpdateManyFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[0\] to have type "array or object" but found "[\w ]+"/'); @@ -237,7 +242,19 @@ public function testUpdateManyFilterArgumentTypeCheck($filter) ]); } - public function testUpdateManyUpdateArgumentMissing() + /** + * @dataProvider provideUpdateDocuments + * @dataProvider provideUpdatePipelines + * @doesNotPerformAssertions + */ + public function testUpdateManyUpdateArgument($update): void + { + new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ + [BulkWrite::UPDATE_MANY => [['x' => 1], $update]], + ]); + } + + public function testUpdateManyUpdateArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Missing second argument for $operations[0]["updateMany"]'); @@ -246,10 +263,8 @@ public function testUpdateManyUpdateArgumentMissing() ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testUpdateManyUpdateArgumentTypeCheck($update) + /** @dataProvider provideInvalidDocumentValues */ + public function testUpdateManyUpdateArgumentTypeCheck($update): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[1\] to have type "array or object" but found "[\w ]+"/'); @@ -258,19 +273,21 @@ public function testUpdateManyUpdateArgumentTypeCheck($update) ]); } - public function testUpdateManyUpdateArgumentRequiresOperatorsOrPipeline() + /** + * @dataProvider provideReplacementDocuments + * @dataProvider provideEmptyUpdatePipelines + */ + public function testUpdateManyUpdateArgumentProhibitsReplacementDocumentOrEmptyPipeline($update): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('First key in $operations[0]["updateMany"][1] is neither an update operator nor a pipeline'); + $this->expectExceptionMessage('Expected update operator(s) or non-empty pipeline for $operations[0]["updateMany"][1]'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ - [BulkWrite::UPDATE_MANY => [['_id' => ['$gt' => 1]], ['x' => 1]]], + [BulkWrite::UPDATE_MANY => [['x' => 1], $update]], ]); } - /** - * @dataProvider provideInvalidArrayValues - */ - public function testUpdateManyArrayFiltersOptionTypeCheck($arrayFilters) + /** @dataProvider provideInvalidArrayValues */ + public function testUpdateManyArrayFiltersOptionTypeCheck($arrayFilters): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[2\]\["arrayFilters"\] to have type "array" but found "[\w ]+"/'); @@ -279,10 +296,8 @@ public function testUpdateManyArrayFiltersOptionTypeCheck($arrayFilters) ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testUpdateManyCollationOptionTypeCheck($collation) + /** @dataProvider provideInvalidDocumentValues */ + public function testUpdateManyCollationOptionTypeCheck($collation): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/'); @@ -291,10 +306,8 @@ public function testUpdateManyCollationOptionTypeCheck($collation) ]); } - /** - * @dataProvider provideInvalidBooleanValues - */ - public function testUpdateManyUpsertOptionTypeCheck($upsert) + /** @dataProvider provideInvalidBooleanValues */ + public function testUpdateManyUpsertOptionTypeCheck($upsert): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/'); @@ -303,7 +316,19 @@ public function testUpdateManyUpsertOptionTypeCheck($upsert) ]); } - public function testUpdateOneFilterArgumentMissing() + /** + * @dataProvider provideUpdateDocuments + * @dataProvider provideUpdatePipelines + * @doesNotPerformAssertions + */ + public function testUpdateOneUpdateArgument($update): void + { + new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ + [BulkWrite::UPDATE_ONE => [['x' => 1], $update]], + ]); + } + + public function testUpdateOneFilterArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Missing first argument for $operations[0]["updateOne"]'); @@ -312,10 +337,8 @@ public function testUpdateOneFilterArgumentMissing() ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testUpdateOneFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testUpdateOneFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[0\] to have type "array or object" but found "[\w ]+"/'); @@ -324,7 +347,7 @@ public function testUpdateOneFilterArgumentTypeCheck($filter) ]); } - public function testUpdateOneUpdateArgumentMissing() + public function testUpdateOneUpdateArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Missing second argument for $operations[0]["updateOne"]'); @@ -333,10 +356,8 @@ public function testUpdateOneUpdateArgumentMissing() ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testUpdateOneUpdateArgumentTypeCheck($update) + /** @dataProvider provideInvalidDocumentValues */ + public function testUpdateOneUpdateArgumentTypeCheck($update): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[1\] to have type "array or object" but found "[\w ]+"/'); @@ -345,19 +366,21 @@ public function testUpdateOneUpdateArgumentTypeCheck($update) ]); } - public function testUpdateOneUpdateArgumentRequiresOperatorsOrPipeline() + /** + * @dataProvider provideReplacementDocuments + * @dataProvider provideEmptyUpdatePipelines + */ + public function testUpdateOneUpdateArgumentProhibitsReplacementDocumentOrEmptyPipeline($update): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('First key in $operations[0]["updateOne"][1] is neither an update operator nor a pipeline'); + $this->expectExceptionMessage('Expected update operator(s) or non-empty pipeline for $operations[0]["updateOne"][1]'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ - [BulkWrite::UPDATE_ONE => [['_id' => 1], ['x' => 1]]], + [BulkWrite::UPDATE_ONE => [['x' => 1], $update]], ]); } - /** - * @dataProvider provideInvalidArrayValues - */ - public function testUpdateOneArrayFiltersOptionTypeCheck($arrayFilters) + /** @dataProvider provideInvalidArrayValues */ + public function testUpdateOneArrayFiltersOptionTypeCheck($arrayFilters): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[2\]\["arrayFilters"\] to have type "array" but found "[\w ]+"/'); @@ -366,10 +389,8 @@ public function testUpdateOneArrayFiltersOptionTypeCheck($arrayFilters) ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testUpdateOneCollationOptionTypeCheck($collation) + /** @dataProvider provideInvalidDocumentValues */ + public function testUpdateOneCollationOptionTypeCheck($collation): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/'); @@ -378,10 +399,8 @@ public function testUpdateOneCollationOptionTypeCheck($collation) ]); } - /** - * @dataProvider provideInvalidBooleanValues - */ - public function testUpdateOneUpsertOptionTypeCheck($upsert) + /** @dataProvider provideInvalidBooleanValues */ + public function testUpdateOneUpsertOptionTypeCheck($upsert): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/'); @@ -390,10 +409,8 @@ public function testUpdateOneUpsertOptionTypeCheck($upsert) ]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new BulkWrite( diff --git a/tests/Operation/CountDocumentsFunctionalTest.php b/tests/Operation/CountDocumentsFunctionalTest.php index 23bba406c..3b3aa7cc1 100644 --- a/tests/Operation/CountDocumentsFunctionalTest.php +++ b/tests/Operation/CountDocumentsFunctionalTest.php @@ -4,16 +4,37 @@ use MongoDB\Operation\CountDocuments; use MongoDB\Operation\InsertMany; +use MongoDB\Tests\CommandObserver; +use stdClass; class CountDocumentsFunctionalTest extends FunctionalTestCase { - public function testEmptyCollection() + /** @dataProvider provideFilterDocuments */ + public function testFilterDocuments($filter, stdClass $expectedMatchStage): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new CountDocuments( + $this->getDatabaseName(), + $this->getCollectionName(), + $filter + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedMatchStage): void { + $this->assertEquals($expectedMatchStage, $event['started']->getCommand()->pipeline[0]->{'$match'} ?? null); + } + ); + } + + public function testEmptyCollection(): void { $operation = new CountDocuments($this->getDatabaseName(), $this->getCollectionName(), []); $this->assertSame(0, $operation->execute($this->getPrimaryServer())); } - public function testNonEmptyCollection() + public function testNonEmptyCollection(): void { $insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [ ['x' => 1], diff --git a/tests/Operation/CountDocumentsTest.php b/tests/Operation/CountDocumentsTest.php index 55fe0bfb8..3fcb9003a 100644 --- a/tests/Operation/CountDocumentsTest.php +++ b/tests/Operation/CountDocumentsTest.php @@ -7,19 +7,15 @@ class CountDocumentsTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); new CountDocuments($this->getDatabaseName(), $this->getCollectionName(), $filter); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new CountDocuments($this->getDatabaseName(), $this->getCollectionName(), [], $options); diff --git a/tests/Operation/CountFunctionalTest.php b/tests/Operation/CountFunctionalTest.php index 245cfdd2e..192dd5434 100644 --- a/tests/Operation/CountFunctionalTest.php +++ b/tests/Operation/CountFunctionalTest.php @@ -6,14 +6,33 @@ use MongoDB\Operation\CreateIndexes; use MongoDB\Operation\InsertMany; use MongoDB\Tests\CommandObserver; -use function version_compare; +use stdClass; class CountFunctionalTest extends FunctionalTestCase { - public function testDefaultReadConcernIsOmitted() + /** @dataProvider provideFilterDocuments */ + public function testFilterDocuments($filter, stdClass $expectedQuery): void { (new CommandObserver())->observe( - function () { + function () use ($filter): void { + $operation = new Count( + $this->getDatabaseName(), + $this->getCollectionName(), + $filter + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->query ?? null); + } + ); + } + + public function testDefaultReadConcernIsOmitted(): void + { + (new CommandObserver())->observe( + function (): void { $operation = new Count( $this->getDatabaseName(), $this->getCollectionName(), @@ -23,13 +42,13 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); } ); } - public function testHintOption() + public function testHintOption(): void { $insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [ ['x' => 1], @@ -49,12 +68,8 @@ public function testHintOption() 'sparse_x', ]; - /* Per SERVER-22041, the count command in server versions before 3.3.2 - * may ignore the hint option if its query predicate is empty. */ - $filter = ['_id' => ['$exists' => true]]; - foreach ($hintsUsingSparseIndex as $hint) { - $operation = new Count($this->getDatabaseName(), $this->getCollectionName(), $filter, ['hint' => $hint]); + $operation = new Count($this->getDatabaseName(), $this->getCollectionName(), [], ['hint' => $hint]); $this->assertSame(2, $operation->execute($this->getPrimaryServer())); } @@ -65,19 +80,15 @@ public function testHintOption() ]; foreach ($hintsNotUsingSparseIndex as $hint) { - $operation = new Count($this->getDatabaseName(), $this->getCollectionName(), $filter, ['hint' => $hint]); + $operation = new Count($this->getDatabaseName(), $this->getCollectionName(), [], ['hint' => $hint]); $this->assertSame(3, $operation->execute($this->getPrimaryServer())); } } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new Count( $this->getDatabaseName(), $this->getCollectionName(), @@ -87,7 +98,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); diff --git a/tests/Operation/CountTest.php b/tests/Operation/CountTest.php index 06be7d4e3..5259a9d44 100644 --- a/tests/Operation/CountTest.php +++ b/tests/Operation/CountTest.php @@ -2,24 +2,21 @@ namespace MongoDB\Tests\Operation; +use MongoDB\Driver\ReadConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Count; class CountTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); new Count($this->getDatabaseName(), $this->getCollectionName(), $filter); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Count($this->getDatabaseName(), $this->getCollectionName(), [], $options); @@ -68,4 +65,31 @@ private function getInvalidHintValues() { return [123, 3.14, true]; } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'hint' => '_id_', + 'limit' => 10, + 'skip' => 20, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'maxTimeMS' => 100, + ]; + $operation = new Count($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $options); + + $expected = [ + 'count' => $this->getCollectionName(), + 'query' => (object) ['x' => 1], + 'collation' => (object) ['locale' => 'fr'], + 'hint' => '_id_', + 'comment' => 'explain me', + 'limit' => 10, + 'skip' => 20, + 'maxTimeMS' => 100, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); + } } diff --git a/tests/Operation/CreateCollectionFunctionalTest.php b/tests/Operation/CreateCollectionFunctionalTest.php index 6874ed736..aa9d9e592 100644 --- a/tests/Operation/CreateCollectionFunctionalTest.php +++ b/tests/Operation/CreateCollectionFunctionalTest.php @@ -4,14 +4,13 @@ use MongoDB\Operation\CreateCollection; use MongoDB\Tests\CommandObserver; -use function version_compare; class CreateCollectionFunctionalTest extends FunctionalTestCase { - public function testDefaultWriteConcernIsOmitted() + public function testDefaultWriteConcernIsOmitted(): void { (new CommandObserver())->observe( - function () { + function (): void { $operation = new CreateCollection( $this->getDatabaseName(), $this->getCollectionName(), @@ -20,20 +19,16 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); } ); } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new CreateCollection( $this->getDatabaseName(), $this->getCollectionName(), @@ -42,7 +37,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); diff --git a/tests/Operation/CreateCollectionTest.php b/tests/Operation/CreateCollectionTest.php index da158eed5..dab0140f9 100644 --- a/tests/Operation/CreateCollectionTest.php +++ b/tests/Operation/CreateCollectionTest.php @@ -7,10 +7,15 @@ class CreateCollectionTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + public function testConstructorPipelineOptionMustBeAList(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('"pipeline" option is not a valid aggregation pipeline'); + new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), ['pipeline' => [1 => ['$match' => ['x' => 1]]]]); + } + + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), $options); @@ -28,10 +33,26 @@ public function provideInvalidConstructorOptions() $options[][] = ['capped' => $value]; } + foreach ($this->getInvalidDocumentValues() as $value) { + $options[][] = ['changeStreamPreAndPostImages' => $value]; + } + + foreach ($this->getInvalidDocumentValues() as $value) { + $options[][] = ['clusteredIndex' => $value]; + } + foreach ($this->getInvalidDocumentValues() as $value) { $options[][] = ['collation' => $value]; } + foreach ($this->getInvalidDocumentValues() as $value) { + $options[][] = ['encryptedFields' => $value]; + } + + foreach ($this->getInvalidIntegerValues() as $value) { + $options[][] = ['expireAfterSeconds' => $value]; + } + foreach ($this->getInvalidIntegerValues() as $value) { $options[][] = ['flags' => $value]; } @@ -48,6 +69,10 @@ public function provideInvalidConstructorOptions() $options[][] = ['maxTimeMS' => $value]; } + foreach ($this->getInvalidArrayValues() as $value) { + $options[][] = ['pipeline' => $value]; + } + foreach ($this->getInvalidSessionValues() as $value) { $options[][] = ['session' => $value]; } @@ -60,6 +85,10 @@ public function provideInvalidConstructorOptions() $options[][] = ['storageEngine' => $value]; } + foreach ($this->getInvalidDocumentValues() as $value) { + $options[][] = ['timeseries' => $value]; + } + foreach ($this->getInvalidArrayValues() as $value) { $options[][] = ['typeMap' => $value]; } @@ -76,6 +105,10 @@ public function provideInvalidConstructorOptions() $options[][] = ['validator' => $value]; } + foreach ($this->getInvalidStringValues() as $value) { + $options[][] = ['viewOn' => $value]; + } + foreach ($this->getInvalidWriteConcernValues() as $value) { $options[][] = ['writeConcern' => $value]; } @@ -83,13 +116,13 @@ public function provideInvalidConstructorOptions() return $options; } - public function testAutoIndexIdOptionIsDeprecated() + public function testAutoIndexIdOptionIsDeprecated(): void { - $this->assertDeprecated(function () { + $this->assertDeprecated(function (): void { new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), ['autoIndexId' => true]); }); - $this->assertDeprecated(function () { + $this->assertDeprecated(function (): void { new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), ['autoIndexId' => false]); }); } diff --git a/tests/Operation/CreateEncryptedCollectionFunctionalTest.php b/tests/Operation/CreateEncryptedCollectionFunctionalTest.php new file mode 100644 index 000000000..b6d920ce4 --- /dev/null +++ b/tests/Operation/CreateEncryptedCollectionFunctionalTest.php @@ -0,0 +1,244 @@ +skipIfClientSideEncryptionIsNotSupported(); + + if (! static::isCryptSharedLibAvailable() && ! static::isMongocryptdAvailable()) { + $this->markTestSkipped('Neither crypt_shared nor mongocryptd are available'); + } + + if ($this->isStandalone() || ($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets())) { + $this->markTestSkipped('Queryable Encryption requires replica sets'); + } + + $this->skipIfServerVersion('<', '6.0.0', 'Queryable Encryption requires MongoDB 6.0 or later'); + + $client = static::createTestClient(); + + /* Since test cases may create encrypted collections, ensure that the + * collection and any "enxcol_" collections do not exist. Specify + * "encryptedFields" to ensure "enxcol_" collections are handled. */ + $client->selectCollection($this->getDatabaseName(), $this->getCollectionName())->drop(['encryptedFields' => []]); + + // Drop the key vault with a majority write concern + $client->selectCollection('keyvault', 'datakeys')->drop(['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]); + + $this->clientEncryption = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + ], + ]); + } + + /** @dataProvider provideEncryptedFieldsAndFieldsIsMissing */ + public function testCreateDataKeysNopIfFieldsIsMissing($input, array $expectedOutput): void + { + $operation = new CreateEncryptedCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + ['encryptedFields' => $input] + ); + + $operation->createDataKeys( + $this->clientEncryption, + 'local', + null, + $encryptedFieldsOutput + ); + + $this->assertSame($expectedOutput, $encryptedFieldsOutput); + } + + public function provideEncryptedFieldsAndFieldsIsMissing(): array + { + $ef = []; + + return [ + 'array' => [$ef, $ef], + 'object' => [(object) $ef, $ef], + 'Serializable' => [new BSONDocument($ef), $ef], + 'Document' => [Document::fromPHP($ef), $ef], + ]; + } + + /** @dataProvider provideEncryptedFieldsAndFieldsHasInvalidType */ + public function testCreateDataKeysNopIfFieldsHasInvalidType($input, array $expectedOutput): void + { + $operation = new CreateEncryptedCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + ['encryptedFields' => $input] + ); + + $operation->createDataKeys( + $this->clientEncryption, + 'local', + null, + $encryptedFieldsOutput + ); + + $this->assertSame($expectedOutput, $encryptedFieldsOutput); + } + + public function provideEncryptedFieldsAndFieldsHasInvalidType(): array + { + $ef = ['fields' => 'not-an-array']; + + return [ + 'array' => [$ef, $ef], + 'object' => [(object) $ef, $ef], + 'Serializable' => [new BSONDocument($ef), $ef], + 'Document' => [Document::fromPHP($ef), $ef], + ]; + } + + /** @dataProvider provideEncryptedFieldsElementHasInvalidType */ + public function testCreateDataKeysSkipsNonDocumentFields($input, array $expectedOutput): void + { + $operation = new CreateEncryptedCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + ['encryptedFields' => $input] + ); + + $operation->createDataKeys( + $this->clientEncryption, + 'local', + null, + $encryptedFieldsOutput + ); + + $this->assertSame($expectedOutput, $encryptedFieldsOutput); + } + + public function provideEncryptedFieldsElementHasInvalidType(): array + { + $ef = ['fields' => ['not-an-array-or-object']]; + + return [ + 'array' => [$ef, $ef], + 'object' => [(object) $ef, $ef], + 'Serializable' => [new BSONDocument(['fields' => new BSONArray(['not-an-array-or-object'])]), $ef], + 'Document' => [Document::fromPHP($ef), $ef], + ]; + } + + public function testCreateDataKeysDoesNotModifyOriginalEncryptedFieldsOption(): void + { + $originalField = (object) ['path' => 'ssn', 'bsonType' => 'string', 'keyId' => null]; + $originalEncryptedFields = (object) ['fields' => [$originalField]]; + + $operation = new CreateEncryptedCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + ['encryptedFields' => $originalEncryptedFields] + ); + + $operation->createDataKeys( + $this->clientEncryption, + 'local', + null, + $modifiedEncryptedFields + ); + + $this->assertSame($originalField, $originalEncryptedFields->fields[0]); + $this->assertNull($originalField->keyId); + + $this->assertInstanceOf(Binary::class, $modifiedEncryptedFields['fields'][0]['keyId'] ?? null); + } + + /** @dataProvider provideEncryptedFields */ + public function testEncryptedFieldsDocuments($input): void + { + $operation = new CreateEncryptedCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + ['encryptedFields' => $input] + ); + + $operation->createDataKeys( + $this->clientEncryption, + 'local', + null, + $modifiedEncryptedFields + ); + + $this->assertInstanceOf(Binary::class, $modifiedEncryptedFields['fields'][0]['keyId'] ?? null); + } + + public function provideEncryptedFields(): array + { + $ef = ['fields' => [['path' => 'ssn', 'bsonType' => 'string', 'keyId' => null]]]; + + return [ + 'array' => [$ef], + 'object' => [(object) $ef], + 'Serializable' => [new BSONDocument(['fields' => new BSONArray([new BSONDocument($ef['fields'][0])])])], + 'Document' => [Document::fromPHP($ef)], + ]; + } + + public static function createTestClient(?string $uri = null, array $options = [], array $driverOptions = []): Client + { + if (isset($driverOptions['autoEncryption']) && getenv('CRYPT_SHARED_LIB_PATH')) { + $driverOptions['autoEncryption']['extraOptions']['cryptSharedLibPath'] = getenv('CRYPT_SHARED_LIB_PATH'); + } + + return parent::createTestClient($uri, $options, $driverOptions); + } + + private static function isCryptSharedLibAvailable(): bool + { + $cryptSharedLibPath = getenv('CRYPT_SHARED_LIB_PATH'); + + if ($cryptSharedLibPath === false) { + return false; + } + + return is_readable($cryptSharedLibPath); + } + + private static function isMongocryptdAvailable(): bool + { + $paths = explode(PATH_SEPARATOR, getenv("PATH")); + + foreach ($paths as $path) { + if (is_executable($path . DIRECTORY_SEPARATOR . 'mongocryptd')) { + return true; + } + } + + return false; + } +} diff --git a/tests/Operation/CreateEncryptedCollectionTest.php b/tests/Operation/CreateEncryptedCollectionTest.php new file mode 100644 index 000000000..49d3f9c56 --- /dev/null +++ b/tests/Operation/CreateEncryptedCollectionTest.php @@ -0,0 +1,32 @@ +expectException(InvalidArgumentException::class); + new CreateEncryptedCollection($this->getDatabaseName(), $this->getCollectionName(), $options); + } + + public function provideInvalidConstructorOptions(): Generator + { + yield 'encryptedFields is required' => [ + [], + ]; + + foreach ($this->getInvalidDocumentValues() as $value) { + yield 'encryptedFields type: ' . get_debug_type($value) => [ + ['encryptedFields' => $value], + ]; + } + } +} diff --git a/tests/Operation/CreateIndexesFunctionalTest.php b/tests/Operation/CreateIndexesFunctionalTest.php index 6f06c6c64..748e33bc1 100644 --- a/tests/Operation/CreateIndexesFunctionalTest.php +++ b/tests/Operation/CreateIndexesFunctionalTest.php @@ -3,21 +3,23 @@ namespace MongoDB\Tests\Operation; use InvalidArgumentException; +use MongoDB\BSON\Document; use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\UnsupportedException; +use MongoDB\Model\BSONDocument; use MongoDB\Model\IndexInfo; use MongoDB\Operation\CreateIndexes; use MongoDB\Operation\ListIndexes; use MongoDB\Tests\CommandObserver; + use function call_user_func; use function is_callable; use function sprintf; -use function version_compare; class CreateIndexesFunctionalTest extends FunctionalTestCase { - public function testCreateSparseUniqueIndex() + public function testCreateSparseUniqueIndex(): void { $indexes = [['key' => ['x' => 1], 'sparse' => true, 'unique' => true]]; @@ -25,14 +27,14 @@ public function testCreateSparseUniqueIndex() $createdIndexNames = $operation->execute($this->getPrimaryServer()); $this->assertSame('x_1', $createdIndexNames[0]); - $this->assertIndexExists('x_1', function (IndexInfo $info) { + $this->assertIndexExists('x_1', function (IndexInfo $info): void { $this->assertTrue($info->isSparse()); $this->assertTrue($info->isUnique()); $this->assertFalse($info->isTtl()); }); } - public function testCreateCompoundIndex() + public function testCreateCompoundIndex(): void { $indexes = [['key' => ['y' => -1, 'z' => 1]]]; @@ -40,14 +42,14 @@ public function testCreateCompoundIndex() $createdIndexNames = $operation->execute($this->getPrimaryServer()); $this->assertSame('y_-1_z_1', $createdIndexNames[0]); - $this->assertIndexExists('y_-1_z_1', function (IndexInfo $info) { + $this->assertIndexExists('y_-1_z_1', function (IndexInfo $info): void { $this->assertFalse($info->isSparse()); $this->assertFalse($info->isUnique()); $this->assertFalse($info->isTtl()); }); } - public function testCreateGeospatialIndex() + public function testCreateGeospatialIndex(): void { $indexes = [['key' => ['g' => '2dsphere', 'z' => 1]]]; @@ -55,14 +57,14 @@ public function testCreateGeospatialIndex() $createdIndexNames = $operation->execute($this->getPrimaryServer()); $this->assertSame('g_2dsphere_z_1', $createdIndexNames[0]); - $this->assertIndexExists('g_2dsphere_z_1', function (IndexInfo $info) { + $this->assertIndexExists('g_2dsphere_z_1', function (IndexInfo $info): void { $this->assertFalse($info->isSparse()); $this->assertFalse($info->isUnique()); $this->assertFalse($info->isTtl()); }); } - public function testCreateTTLIndex() + public function testCreateTTLIndex(): void { $indexes = [['key' => ['t' => 1], 'expireAfterSeconds' => 0, 'name' => 'my_ttl']]; @@ -70,22 +72,23 @@ public function testCreateTTLIndex() $createdIndexNames = $operation->execute($this->getPrimaryServer()); $this->assertSame('my_ttl', $createdIndexNames[0]); - $this->assertIndexExists('my_ttl', function (IndexInfo $info) { + $this->assertIndexExists('my_ttl', function (IndexInfo $info): void { $this->assertFalse($info->isSparse()); $this->assertFalse($info->isUnique()); $this->assertTrue($info->isTtl()); }); } - public function testCreateIndexes() + /** @dataProvider provideKeyCasts */ + public function testCreateIndexes(callable $cast): void { $expectedNames = ['x_1', 'y_-1_z_1', 'g_2dsphere_z_1', 'my_ttl']; $indexes = [ - ['key' => ['x' => 1], 'sparse' => true, 'unique' => true], - ['key' => ['y' => -1, 'z' => 1]], - ['key' => ['g' => '2dsphere', 'z' => 1]], - ['key' => ['t' => 1], 'expireAfterSeconds' => 0, 'name' => 'my_ttl'], + ['key' => $cast(['x' => 1]), 'sparse' => true, 'unique' => true], + ['key' => $cast(['y' => -1, 'z' => 1])], + ['key' => $cast(['g' => '2dsphere', 'z' => 1])], + ['key' => $cast(['t' => 1]), 'expireAfterSeconds' => 0, 'name' => 'my_ttl'], ]; $operation = new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), $indexes); @@ -93,32 +96,46 @@ public function testCreateIndexes() $this->assertSame($expectedNames, $createdIndexNames); - $this->assertIndexExists('x_1', function (IndexInfo $info) { + $this->assertIndexExists('x_1', function (IndexInfo $info): void { $this->assertTrue($info->isSparse()); $this->assertTrue($info->isUnique()); $this->assertFalse($info->isTtl()); }); - $this->assertIndexExists('y_-1_z_1', function (IndexInfo $info) { + $this->assertIndexExists('y_-1_z_1', function (IndexInfo $info): void { $this->assertFalse($info->isSparse()); $this->assertFalse($info->isUnique()); $this->assertFalse($info->isTtl()); }); - $this->assertIndexExists('g_2dsphere_z_1', function (IndexInfo $info) { + $this->assertIndexExists('g_2dsphere_z_1', function (IndexInfo $info): void { $this->assertFalse($info->isSparse()); $this->assertFalse($info->isUnique()); $this->assertFalse($info->isTtl()); }); - $this->assertIndexExists('my_ttl', function (IndexInfo $info) { + $this->assertIndexExists('my_ttl', function (IndexInfo $info): void { $this->assertFalse($info->isSparse()); $this->assertFalse($info->isUnique()); $this->assertTrue($info->isTtl()); }); } - public function testCreateConflictingIndexesWithCommand() + public function provideKeyCasts(): array + { + // phpcs:disable SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing + // phpcs:disable Squiz.Functions.MultiLineFunctionDeclaration + // phpcs:disable Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore + return [ + 'array' => [function ($key) { return $key; }], + 'object' => [function ($key) { return (object) $key; }], + 'Serializable' => [function ($key) { return new BSONDocument($key); }], + 'Document' => [function ($key) { return Document::fromPHP($key); }], + ]; + // phpcs:enable + } + + public function testCreateConflictingIndexesWithCommand(): void { $indexes = [ ['key' => ['x' => 1], 'sparse' => true, 'unique' => false], @@ -131,10 +148,10 @@ public function testCreateConflictingIndexesWithCommand() $operation->execute($this->getPrimaryServer()); } - public function testDefaultWriteConcernIsOmitted() + public function testDefaultWriteConcernIsOmitted(): void { (new CommandObserver())->observe( - function () { + function (): void { $operation = new CreateIndexes( $this->getDatabaseName(), $this->getCollectionName(), @@ -144,20 +161,16 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); } ); } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new CreateIndexes( $this->getDatabaseName(), $this->getCollectionName(), @@ -167,24 +180,22 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); } - public function testCommitQuorumOption() + public function testCommitQuorumOption(): void { - if (version_compare($this->getServerVersion(), '4.3.4', '<')) { - $this->markTestSkipped('commitQuorum is not supported'); - } + $this->skipIfServerVersion('<', '4.3.4', 'commitQuorum is not supported'); if ($this->getPrimaryServer()->getType() !== Server::TYPE_RS_PRIMARY) { $this->markTestSkipped('commitQuorum is only supported on replica sets'); } (new CommandObserver())->observe( - function () { + function (): void { $operation = new CreateIndexes( $this->getDatabaseName(), $this->getCollectionName(), @@ -194,17 +205,15 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('commitQuorum', $event['started']->getCommand()); } ); } - public function testCommitQuorumUnsupported() + public function testCommitQuorumUnsupported(): void { - if (version_compare($this->getServerVersion(), '4.3.4', '>=')) { - $this->markTestSkipped('commitQuorum is supported'); - } + $this->skipIfServerVersion('>=', '4.3.4', 'commitQuorum is supported'); $operation = new CreateIndexes( $this->getDatabaseName(), @@ -226,11 +235,8 @@ public function testCommitQuorumUnsupported() * argument as its first and only parameter. If an IndexInfo matching the * given name is found, it will be passed to the callback, which may perform * additional assertions. - * - * @param string $indexName - * @param callable $callback */ - private function assertIndexExists($indexName, $callback = null) + private function assertIndexExists(string $indexName, ?callable $callback = null): void { if ($callback !== null && ! is_callable($callback)) { throw new InvalidArgumentException('$callback is not a callable'); diff --git a/tests/Operation/CreateIndexesTest.php b/tests/Operation/CreateIndexesTest.php index 9ee0258a4..74c386f30 100644 --- a/tests/Operation/CreateIndexesTest.php +++ b/tests/Operation/CreateIndexesTest.php @@ -2,23 +2,24 @@ namespace MongoDB\Tests\Operation; +use Generator; +use MongoDB\BSON\Document; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Model\BSONDocument; use MongoDB\Operation\CreateIndexes; use stdClass; class CreateIndexesTest extends TestCase { - public function testConstructorIndexesArgumentMustBeAList() + public function testConstructorIndexesArgumentMustBeAList(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$indexes is not a list (unexpected index: "1")'); + $this->expectExceptionMessage('$indexes is not a list'); new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [1 => ['key' => ['x' => 1]]]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [['key' => ['x' => 1]]], $options); @@ -47,19 +48,18 @@ public function provideInvalidConstructorOptions() return $options; } - public function testConstructorRequiresAtLeastOneIndex() + public function testConstructorRequiresAtLeastOneIndex(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$indexes is empty'); new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), []); } - /** - * @dataProvider provideInvalidIndexSpecificationTypes - */ - public function testConstructorRequiresIndexSpecificationsToBeAnArray($index) + /** @dataProvider provideInvalidIndexSpecificationTypes */ + public function testConstructorRequiresIndexSpecificationsToBeAnArray($index): void { $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected $index[0] to have type "array"'); new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [$index]); } @@ -67,4 +67,52 @@ public function provideInvalidIndexSpecificationTypes() { return $this->wrapValuesForDataProvider($this->getInvalidArrayValues()); } + + public function testConstructorRequiresIndexSpecificationKey(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Required "key" document is missing from index specification'); + new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [[]]); + } + + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorRequiresIndexSpecificationKeyToBeADocument($key): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected "key" option to have type "array or object"'); + new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [['key' => $key]]); + } + + /** @dataProvider provideKeyDocumentsWithInvalidOrder */ + public function testConstructorValidatesIndexSpecificationKeyOrder($key): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected order value for "x" field within "key" option to have type "numeric or string"'); + new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [['key' => $key]]); + } + + public function provideKeyDocumentsWithInvalidOrder(): Generator + { + $invalidOrderValues = [true, [], new stdClass(), null]; + + foreach ($invalidOrderValues as $order) { + yield [['x' => $order]]; + yield [(object) ['x' => $order]]; + yield [new BSONDocument(['x' => $order])]; + yield [Document::fromPHP(['x' => $order])]; + } + } + + /** @dataProvider provideInvalidStringValues */ + public function testConstructorRequiresIndexSpecificationNameToBeString($name): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected "name" option to have type "string"'); + new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [['key' => ['x' => 1], 'name' => $name]]); + } + + public function provideInvalidStringValues() + { + return $this->wrapValuesForDataProvider($this->getInvalidStringValues()); + } } diff --git a/tests/Operation/DatabaseCommandFunctionalTest.php b/tests/Operation/DatabaseCommandFunctionalTest.php index 0d8d2ec28..f113269d0 100644 --- a/tests/Operation/DatabaseCommandFunctionalTest.php +++ b/tests/Operation/DatabaseCommandFunctionalTest.php @@ -2,20 +2,50 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; +use MongoDB\Driver\Command; +use MongoDB\Model\BSONDocument; use MongoDB\Operation\DatabaseCommand; use MongoDB\Tests\CommandObserver; -use function version_compare; class DatabaseCommandFunctionalTest extends FunctionalTestCase { - public function testSessionOption() + /** @dataProvider provideCommandDocuments */ + public function testCommandDocuments($command): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } + (new CommandObserver())->observe( + function () use ($command): void { + $operation = new DatabaseCommand( + $this->getDatabaseName(), + $command + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event): void { + $this->assertEquals(1, $event['started']->getCommand()->ping ?? null); + } + ); + } + public function provideCommandDocuments(): array + { + return [ + 'array' => [['ping' => 1]], + 'object' => [(object) ['ping' => 1]], + 'Serializable' => [new BSONDocument(['ping' => 1])], + 'Document' => [Document::fromPHP(['ping' => 1])], + 'Command:array' => [new Command(['ping' => 1])], + 'Command:object' => [new Command((object) ['ping' => 1])], + 'Command:Serializable' => [new Command(new BSONDocument(['ping' => 1]))], + 'Command:Document' => [new Command(Document::fromPHP(['ping' => 1]))], + ]; + } + + public function testSessionOption(): void + { (new CommandObserver())->observe( - function () { + function (): void { $operation = new DatabaseCommand( $this->getDatabaseName(), ['ping' => 1], @@ -24,7 +54,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); diff --git a/tests/Operation/DatabaseCommandTest.php b/tests/Operation/DatabaseCommandTest.php index e4f82c63c..29df6bd07 100644 --- a/tests/Operation/DatabaseCommandTest.php +++ b/tests/Operation/DatabaseCommandTest.php @@ -7,19 +7,15 @@ class DatabaseCommandTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorCommandArgumentTypeCheck($command) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorCommandArgumentTypeCheck($command): void { $this->expectException(InvalidArgumentException::class); new DatabaseCommand($this->getDatabaseName(), $command); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new DatabaseCommand($this->getDatabaseName(), ['ping' => 1], $options); diff --git a/tests/Operation/DeleteFunctionalTest.php b/tests/Operation/DeleteFunctionalTest.php index a11846caf..4ae84045e 100644 --- a/tests/Operation/DeleteFunctionalTest.php +++ b/tests/Operation/DeleteFunctionalTest.php @@ -7,26 +7,44 @@ use MongoDB\Driver\BulkWrite; use MongoDB\Driver\WriteConcern; use MongoDB\Exception\BadMethodCallException; +use MongoDB\Exception\UnsupportedException; use MongoDB\Operation\Delete; use MongoDB\Tests\CommandObserver; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; -use function version_compare; +use stdClass; class DeleteFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; - /** @var Collection */ private $collection; - private function doSetUp() + public function setUp(): void { parent::setUp(); $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); } - public function testDeleteOne() + /** @dataProvider provideFilterDocuments */ + public function testFilterDocuments($filter, stdClass $expectedQuery): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new Delete( + $this->getDatabaseName(), + $this->getCollectionName(), + $filter, + 1 + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->deletes[0]->q ?? null); + } + ); + } + + public function testDeleteOne(): void { $this->createFixtures(3); @@ -46,7 +64,7 @@ public function testDeleteOne() $this->assertSameDocuments($expected, $this->collection->find()); } - public function testDeleteMany() + public function testDeleteMany(): void { $this->createFixtures(3); @@ -65,14 +83,28 @@ public function testDeleteMany() $this->assertSameDocuments($expected, $this->collection->find()); } - public function testSessionOption() + public function testHintOptionAndUnacknowledgedWriteConcernUnsupportedClientSideError(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } + $this->skipIfServerVersion('>=', '4.4.0', 'hint is supported'); + + $operation = new Delete( + $this->getDatabaseName(), + $this->getCollectionName(), + [], + 0, + ['hint' => '_id_', 'writeConcern' => new WriteConcern(0)] + ); + + $this->expectException(UnsupportedException::class); + $this->expectExceptionMessage('Hint is not supported by the server executing this operation'); + $operation->execute($this->getPrimaryServer()); + } + + public function testSessionOption(): void + { (new CommandObserver())->observe( - function () { + function (): void { $operation = new Delete( $this->getDatabaseName(), $this->getCollectionName(), @@ -83,7 +115,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); @@ -102,10 +134,8 @@ public function testUnacknowledgedWriteConcern() return $result; } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesDeletedCount(DeleteResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesDeletedCount(DeleteResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); @@ -114,10 +144,8 @@ public function testUnacknowledgedWriteConcernAccessesDeletedCount(DeleteResult /** * Create data fixtures. - * - * @param integer $n */ - private function createFixtures($n) + private function createFixtures(int $n): void { $bulkWrite = new BulkWrite(['ordered' => true]); diff --git a/tests/Operation/DeleteTest.php b/tests/Operation/DeleteTest.php index c22a28801..90b9cae52 100644 --- a/tests/Operation/DeleteTest.php +++ b/tests/Operation/DeleteTest.php @@ -1,26 +1,35 @@ expectException(InvalidArgumentException::class); new Delete($this->getDatabaseName(), $this->getCollectionName(), $filter, 0); } - /** - * @dataProvider provideInvalidLimitValues - */ - public function testConstructorLimitArgumentMustBeOneOrZero($limit) + /** @dataProvider provideInvalidIntegerValues */ + public function testConstructorLimitArgumentMustBeInt($limit): void + { + $this->expectException(TypeError::class); + new Delete($this->getDatabaseName(), $this->getCollectionName(), [], $limit); + } + + /** @dataProvider provideInvalidLimitValues */ + public function testConstructorLimitArgumentMustBeOneOrZero($limit): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$limit must be 0 or 1'); @@ -29,13 +38,11 @@ public function testConstructorLimitArgumentMustBeOneOrZero($limit) public function provideInvalidLimitValues() { - return $this->wrapValuesForDataProvider(array_merge($this->getInvalidIntegerValues(), [-1, 2])); + return $this->wrapValuesForDataProvider([-1, 2]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Delete($this->getDatabaseName(), $this->getCollectionName(), [], 1, $options); @@ -59,4 +66,32 @@ public function provideInvalidConstructorOptions() return $options; } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'collation' => ['locale' => 'fr'], + 'hint' => '_id_', + 'let' => ['a' => 1], + 'comment' => 'explain me', + // Intentionally omitted options + 'writeConcern' => new WriteConcern(0), + ]; + $operation = new Delete($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], 0, $options); + + $expected = [ + 'delete' => $this->getCollectionName(), + 'deletes' => [ + [ + 'q' => ['x' => 1], + 'limit' => 0, + 'collation' => (object) ['locale' => 'fr'], + 'hint' => '_id_', + ], + ], + 'comment' => 'explain me', + 'let' => (object) ['a' => 1], + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); + } } diff --git a/tests/Operation/DistinctFunctionalTest.php b/tests/Operation/DistinctFunctionalTest.php index f47f61faf..53e411a0f 100644 --- a/tests/Operation/DistinctFunctionalTest.php +++ b/tests/Operation/DistinctFunctionalTest.php @@ -5,17 +5,38 @@ use MongoDB\Driver\BulkWrite; use MongoDB\Operation\Distinct; use MongoDB\Tests\CommandObserver; +use stdClass; + use function is_scalar; use function json_encode; use function usort; -use function version_compare; class DistinctFunctionalTest extends FunctionalTestCase { - public function testDefaultReadConcernIsOmitted() + /** @dataProvider provideFilterDocuments */ + public function testFilterDocuments($filter, stdClass $expectedQuery): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new Distinct( + $this->getDatabaseName(), + $this->getCollectionName(), + 'x', + $filter + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->query ?? null); + } + ); + } + + public function testDefaultReadConcernIsOmitted(): void { (new CommandObserver())->observe( - function () { + function (): void { $operation = new Distinct( $this->getDatabaseName(), $this->getCollectionName(), @@ -26,20 +47,16 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); } ); } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new Distinct( $this->getDatabaseName(), $this->getCollectionName(), @@ -50,16 +67,14 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ - public function testTypeMapOption(array $typeMap, array $expectedDocuments) + /** @dataProvider provideTypeMapOptionsAndExpectedDocuments */ + public function testTypeMapOption(array $typeMap, array $expectedDocuments): void { $bulkWrite = new BulkWrite(['ordered' => true]); $bulkWrite->insert([ diff --git a/tests/Operation/DistinctTest.php b/tests/Operation/DistinctTest.php index 95ea2537a..9ece08fa5 100644 --- a/tests/Operation/DistinctTest.php +++ b/tests/Operation/DistinctTest.php @@ -2,24 +2,22 @@ namespace MongoDB\Tests\Operation; +use MongoDB\Driver\ReadConcern; +use MongoDB\Driver\ReadPreference; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Distinct; class DistinctTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'x', $filter); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'x', [], $options); @@ -55,4 +53,29 @@ public function provideInvalidConstructorOptions() return $options; } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'collation' => ['locale' => 'fr'], + 'maxTimeMS' => 100, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + 'comment' => 'explain me', + // Intentionally omitted options + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), + 'typeMap' => ['root' => 'array'], + ]; + $operation = new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'f', ['x' => 1], $options); + + $expected = [ + 'distinct' => $this->getCollectionName(), + 'key' => 'f', + 'query' => (object) ['x' => 1], + 'collation' => (object) ['locale' => 'fr'], + 'comment' => 'explain me', + 'maxTimeMS' => 100, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); + } } diff --git a/tests/Operation/DropCollectionFunctionalTest.php b/tests/Operation/DropCollectionFunctionalTest.php index a52a7c3ba..08a5af222 100644 --- a/tests/Operation/DropCollectionFunctionalTest.php +++ b/tests/Operation/DropCollectionFunctionalTest.php @@ -4,17 +4,14 @@ use MongoDB\Operation\DropCollection; use MongoDB\Operation\InsertOne; -use MongoDB\Operation\ListCollections; use MongoDB\Tests\CommandObserver; -use function sprintf; -use function version_compare; class DropCollectionFunctionalTest extends FunctionalTestCase { - public function testDefaultWriteConcernIsOmitted() + public function testDefaultWriteConcernIsOmitted(): void { (new CommandObserver())->observe( - function () { + function (): void { $operation = new DropCollection( $this->getDatabaseName(), $this->getCollectionName(), @@ -23,13 +20,13 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); } ); } - public function testDropExistingCollection() + public function testDropExistingCollection(): void { $server = $this->getPrimaryServer(); @@ -44,10 +41,8 @@ public function testDropExistingCollection() $this->assertCollectionDoesNotExist($this->getCollectionName()); } - /** - * @depends testDropExistingCollection - */ - public function testDropNonexistentCollection() + /** @depends testDropExistingCollection */ + public function testDropNonexistentCollection(): void { $this->assertCollectionDoesNotExist($this->getCollectionName()); @@ -59,14 +54,10 @@ public function testDropNonexistentCollection() $this->assertIsObject($commandResult); } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new DropCollection( $this->getDatabaseName(), $this->getCollectionName(), @@ -75,32 +66,9 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); } - - /** - * Asserts that a collection with the given name does not exist on the - * server. - * - * @param string $collectionName - */ - private function assertCollectionDoesNotExist($collectionName) - { - $operation = new ListCollections($this->getDatabaseName()); - $collections = $operation->execute($this->getPrimaryServer()); - - $foundCollection = null; - - foreach ($collections as $collection) { - if ($collection->getName() === $collectionName) { - $foundCollection = $collection; - break; - } - } - - $this->assertNull($foundCollection, sprintf('Collection %s exists', $collectionName)); - } } diff --git a/tests/Operation/DropCollectionTest.php b/tests/Operation/DropCollectionTest.php index 61d9359fa..cde87ae44 100644 --- a/tests/Operation/DropCollectionTest.php +++ b/tests/Operation/DropCollectionTest.php @@ -7,10 +7,8 @@ class DropCollectionTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new DropCollection($this->getDatabaseName(), $this->getCollectionName(), $options); diff --git a/tests/Operation/DropDatabaseFunctionalTest.php b/tests/Operation/DropDatabaseFunctionalTest.php index fab1cbdcd..8ee86b3b1 100644 --- a/tests/Operation/DropDatabaseFunctionalTest.php +++ b/tests/Operation/DropDatabaseFunctionalTest.php @@ -7,15 +7,15 @@ use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListDatabases; use MongoDB\Tests\CommandObserver; + use function sprintf; -use function version_compare; class DropDatabaseFunctionalTest extends FunctionalTestCase { - public function testDefaultWriteConcernIsOmitted() + public function testDefaultWriteConcernIsOmitted(): void { (new CommandObserver())->observe( - function () { + function (): void { $operation = new DropDatabase( $this->getDatabaseName(), ['writeConcern' => $this->createDefaultWriteConcern()] @@ -23,13 +23,13 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); } ); } - public function testDropExistingDatabase() + public function testDropExistingDatabase(): void { $server = $this->getPrimaryServer(); @@ -43,10 +43,8 @@ public function testDropExistingDatabase() $this->assertDatabaseDoesNotExist($server, $this->getDatabaseName()); } - /** - * @depends testDropExistingDatabase - */ - public function testDropNonexistentDatabase() + /** @depends testDropExistingDatabase */ + public function testDropNonexistentDatabase(): void { $server = $this->getPrimaryServer(); @@ -59,14 +57,10 @@ public function testDropNonexistentDatabase() $operation->execute($server); } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new DropDatabase( $this->getDatabaseName(), ['session' => $this->createSession()] @@ -74,7 +68,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); @@ -82,11 +76,8 @@ function (array $event) { /** * Asserts that a database with the given name does not exist on the server. - * - * @param Server $server - * @param string $databaseName */ - private function assertDatabaseDoesNotExist(Server $server, $databaseName) + private function assertDatabaseDoesNotExist(Server $server, string $databaseName): void { $operation = new ListDatabases(); $databases = $operation->execute($server); diff --git a/tests/Operation/DropDatabaseTest.php b/tests/Operation/DropDatabaseTest.php index 45d159b0b..fd3a527fd 100644 --- a/tests/Operation/DropDatabaseTest.php +++ b/tests/Operation/DropDatabaseTest.php @@ -7,10 +7,8 @@ class DropDatabaseTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new DropDatabase($this->getDatabaseName(), $options); diff --git a/tests/Operation/DropEncryptedCollectionTest.php b/tests/Operation/DropEncryptedCollectionTest.php new file mode 100644 index 000000000..31a559efc --- /dev/null +++ b/tests/Operation/DropEncryptedCollectionTest.php @@ -0,0 +1,32 @@ +expectException(InvalidArgumentException::class); + new DropEncryptedCollection($this->getDatabaseName(), $this->getCollectionName(), $options); + } + + public function provideInvalidConstructorOptions(): Generator + { + yield 'encryptedFields is required' => [ + [], + ]; + + foreach ($this->getInvalidDocumentValues() as $value) { + yield 'encryptedFields type: ' . get_debug_type($value) => [ + ['encryptedFields' => $value], + ]; + } + } +} diff --git a/tests/Operation/DropIndexesFunctionalTest.php b/tests/Operation/DropIndexesFunctionalTest.php index b9d104c14..a7cecf548 100644 --- a/tests/Operation/DropIndexesFunctionalTest.php +++ b/tests/Operation/DropIndexesFunctionalTest.php @@ -8,20 +8,20 @@ use MongoDB\Operation\DropIndexes; use MongoDB\Operation\ListIndexes; use MongoDB\Tests\CommandObserver; + use function call_user_func; use function is_callable; use function sprintf; -use function version_compare; class DropIndexesFunctionalTest extends FunctionalTestCase { - public function testDefaultWriteConcernIsOmitted() + public function testDefaultWriteConcernIsOmitted(): void { $operation = new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [['key' => ['x' => 1]]]); $operation->execute($this->getPrimaryServer()); (new CommandObserver())->observe( - function () { + function (): void { $operation = new DropIndexes( $this->getDatabaseName(), $this->getCollectionName(), @@ -31,13 +31,13 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); } ); } - public function testDropOneIndexByName() + public function testDropOneIndexByName(): void { $indexes = [['key' => ['x' => 1]]]; @@ -60,7 +60,7 @@ public function testDropOneIndexByName() } } - public function testDropAllIndexesByWildcard() + public function testDropAllIndexesByWildcard(): void { $indexes = [ ['key' => ['x' => 1]], @@ -92,7 +92,7 @@ public function testDropAllIndexesByWildcard() } } - public function testDropByIndexInfo() + public function testDropByIndexInfo(): void { $info = new IndexInfo([ 'v' => 1, @@ -120,17 +120,13 @@ public function testDropByIndexInfo() } } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - $operation = new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [['key' => ['x' => 1]]]); $operation->execute($this->getPrimaryServer()); (new CommandObserver())->observe( - function () { + function (): void { $operation = new DropIndexes( $this->getDatabaseName(), $this->getCollectionName(), @@ -140,7 +136,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); @@ -153,10 +149,8 @@ function (array $event) { * argument as its first and only parameter. If an IndexInfo matching the * given name is found, it will be passed to the callback, which may perform * additional assertions. - * - * @param callable $callback */ - private function assertIndexExists($indexName, $callback = null) + private function assertIndexExists($indexName, ?callable $callback = null): void { if ($callback !== null && ! is_callable($callback)) { throw new InvalidArgumentException('$callback is not a callable'); diff --git a/tests/Operation/DropIndexesTest.php b/tests/Operation/DropIndexesTest.php index bf947f862..03da89f84 100644 --- a/tests/Operation/DropIndexesTest.php +++ b/tests/Operation/DropIndexesTest.php @@ -7,16 +7,14 @@ class DropIndexesTest extends TestCase { - public function testDropIndexShouldNotAllowEmptyIndexName() + public function testDropIndexShouldNotAllowEmptyIndexName(): void { $this->expectException(InvalidArgumentException::class); new DropIndexes($this->getDatabaseName(), $this->getCollectionName(), ''); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new DropIndexes($this->getDatabaseName(), $this->getCollectionName(), '*', $options); diff --git a/tests/Operation/EstimatedDocumentCountTest.php b/tests/Operation/EstimatedDocumentCountTest.php index ea8231a26..485cb263e 100644 --- a/tests/Operation/EstimatedDocumentCountTest.php +++ b/tests/Operation/EstimatedDocumentCountTest.php @@ -7,10 +7,8 @@ class EstimatedDocumentCountTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new EstimatedDocumentCount($this->getDatabaseName(), $this->getCollectionName(), $options); diff --git a/tests/Operation/ExplainFunctionalTest.php b/tests/Operation/ExplainFunctionalTest.php index e10c5157d..aa28ce8f7 100644 --- a/tests/Operation/ExplainFunctionalTest.php +++ b/tests/Operation/ExplainFunctionalTest.php @@ -5,7 +5,6 @@ use MongoDB\Driver\BulkWrite; use MongoDB\Operation\Aggregate; use MongoDB\Operation\Count; -use MongoDB\Operation\CreateCollection; use MongoDB\Operation\Delete; use MongoDB\Operation\DeleteMany; use MongoDB\Operation\DeleteOne; @@ -21,14 +20,11 @@ use MongoDB\Operation\UpdateMany; use MongoDB\Operation\UpdateOne; use MongoDB\Tests\CommandObserver; -use function version_compare; class ExplainFunctionalTest extends FunctionalTestCase { - /** - * @dataProvider provideVerbosityInformation - */ - public function testCount($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testCount($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -40,10 +36,8 @@ public function testCount($verbosity, $executionStatsExpected, $allPlansExecutio $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testDelete($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testDelete($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -57,10 +51,8 @@ public function testDelete($verbosity, $executionStatsExpected, $allPlansExecuti $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testDeleteMany($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testDeleteMany($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -74,10 +66,8 @@ public function testDeleteMany($verbosity, $executionStatsExpected, $allPlansExe $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testDeleteOne($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testDeleteOne($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -91,15 +81,9 @@ public function testDeleteOne($verbosity, $executionStatsExpected, $allPlansExec $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testDistinct($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testDistinct($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('Explaining distinct command requires server version >= 3.2'); - } - $operation = new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'x', []); $explainOperation = new Explain($this->getDatabaseName(), $operation, ['verbosity' => $verbosity, 'typeMap' => ['root' => 'array', 'document' => 'array']]); @@ -108,15 +92,9 @@ public function testDistinct($verbosity, $executionStatsExpected, $allPlansExecu $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testFindAndModify($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testFindAndModify($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('Explaining findAndModify command requires server version >= 3.2'); - } - $operation = new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), ['remove' => true]); $explainOperation = new Explain($this->getDatabaseName(), $operation, ['verbosity' => $verbosity, 'typeMap' => ['root' => 'array', 'document' => 'array']]); @@ -125,10 +103,8 @@ public function testFindAndModify($verbosity, $executionStatsExpected, $allPlans $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testFind($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testFind($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -140,12 +116,8 @@ public function testFind($verbosity, $executionStatsExpected, $allPlansExecution $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - public function testFindMaxAwait() + public function testFindMaxAwait(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('maxAwaitTimeMS option is not supported'); - } - $maxAwaitTimeMS = 100; /* Calculate an approximate pivot to use for time assertions. We will @@ -162,19 +134,18 @@ public function testFindMaxAwait() 'size' => 1048576, ]; - $operation = new CreateCollection($databaseName, $cappedCollectionName, $cappedCollectionOptions); - $operation->execute($this->getPrimaryServer()); + $this->createCollection($databaseName, $cappedCollectionName, $cappedCollectionOptions); $this->createFixtures(2); $operation = new Find($databaseName, $cappedCollectionName, [], ['cursorType' => Find::TAILABLE_AWAIT, 'maxAwaitTimeMS' => $maxAwaitTimeMS]); (new CommandObserver())->observe( - function () use ($operation) { + function () use ($operation): void { $explainOperation = new Explain($this->getDatabaseName(), $operation, ['typeMap' => ['root' => 'array', 'document' => 'array']]); $explainOperation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $command = $event['started']->getCommand(); $this->assertObjectNotHasAttribute('maxAwaitTimeMS', $command->explain); $this->assertObjectHasAttribute('tailable', $command->explain); @@ -183,7 +154,7 @@ function (array $event) { ); } - public function testFindModifiers() + public function testFindModifiers(): void { $this->createFixtures(3); @@ -195,11 +166,11 @@ public function testFindModifiers() ); (new CommandObserver())->observe( - function () use ($operation) { + function () use ($operation): void { $explainOperation = new Explain($this->getDatabaseName(), $operation, ['typeMap' => ['root' => 'array', 'document' => 'array']]); $explainOperation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $command = $event['started']->getCommand(); $this->assertObjectHasAttribute('sort', $command->explain); $this->assertObjectNotHasAttribute('modifiers', $command->explain); @@ -207,10 +178,8 @@ function (array $event) { ); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testFindOne($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testFindOne($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(1); @@ -222,15 +191,9 @@ public function testFindOne($verbosity, $executionStatsExpected, $allPlansExecut $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testFindOneAndDelete($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testFindOneAndDelete($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('Explaining findOneAndDelete command requires server version >= 3.2'); - } - $operation = new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), []); $explainOperation = new Explain($this->getDatabaseName(), $operation, ['verbosity' => $verbosity, 'typeMap' => ['root' => 'array', 'document' => 'array']]); @@ -239,15 +202,9 @@ public function testFindOneAndDelete($verbosity, $executionStatsExpected, $allPl $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testFindOneAndReplace($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testFindOneAndReplace($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('Explaining findOneAndReplace command requires server version >= 3.2'); - } - $operation = new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1.1], ['x' => 5]); $explainOperation = new Explain($this->getDatabaseName(), $operation, ['verbosity' => $verbosity, 'typeMap' => ['root' => 'array', 'document' => 'array']]); @@ -256,15 +213,9 @@ public function testFindOneAndReplace($verbosity, $executionStatsExpected, $allP $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testFindOneAndUpdate($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testFindOneAndUpdate($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('Explaining findOneAndUpdate command requires server version >= 3.2'); - } - $operation = new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], ['$rename' => ['x' => 'y']]); $explainOperation = new Explain($this->getDatabaseName(), $operation, ['verbosity' => $verbosity, 'typeMap' => ['root' => 'array', 'document' => 'array']]); @@ -273,10 +224,8 @@ public function testFindOneAndUpdate($verbosity, $executionStatsExpected, $allPl $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testUpdate($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testUpdate($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -291,16 +240,12 @@ public function testUpdate($verbosity, $executionStatsExpected, $allPlansExecuti $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - public function testUpdateBypassDocumentValidationSetWhenTrue() + public function testUpdateBypassDocumentValidationSetWhenTrue(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - $this->createFixtures(3); (new CommandObserver())->observe( - function () { + function (): void { $operation = new Update( $this->getDatabaseName(), $this->getCollectionName(), @@ -312,7 +257,7 @@ function () { $explainOperation = new Explain($this->getDatabaseName(), $operation); $result = $explainOperation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute( 'bypassDocumentValidation', $event['started']->getCommand()->explain @@ -322,16 +267,12 @@ function (array $event) { ); } - public function testUpdateBypassDocumentValidationUnsetWhenFalse() + public function testUpdateBypassDocumentValidationUnsetWhenFalse(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - $this->createFixtures(3); (new CommandObserver())->observe( - function () { + function (): void { $operation = new Update( $this->getDatabaseName(), $this->getCollectionName(), @@ -343,7 +284,7 @@ function () { $explainOperation = new Explain($this->getDatabaseName(), $operation); $result = $explainOperation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute( 'bypassDocumentValidation', $event['started']->getCommand()->explain @@ -352,10 +293,8 @@ function (array $event) { ); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testUpdateMany($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testUpdateMany($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -370,10 +309,8 @@ public function testUpdateMany($verbosity, $executionStatsExpected, $allPlansExe $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testUpdateOne($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testUpdateOne($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -388,15 +325,14 @@ public function testUpdateOne($verbosity, $executionStatsExpected, $allPlansExec $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - public function testAggregate() + public function testAggregate(): void { - if (version_compare($this->getServerVersion(), '4.0.0', '<')) { - $this->markTestSkipped('Explaining aggregate command requires server version >= 4.0'); - } + $this->skipIfServerVersion('<', '4.0.0', 'Explaining aggregate command requires server version >= 4.0'); $this->createFixtures(3); - $pipeline = [['$group' => ['_id' => null]]]; + // Use a $sort stage to ensure the aggregate does not get optimised to a query + $pipeline = [['$group' => ['_id' => null]], ['$sort' => ['_id' => 1]]]; $operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), $pipeline); $explainOperation = new Explain($this->getDatabaseName(), $operation, ['verbosity' => Explain::VERBOSITY_QUERY, 'typeMap' => ['root' => 'array', 'document' => 'array']]); @@ -405,14 +341,10 @@ public function testAggregate() $this->assertExplainResult($result, false, false, true); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testAggregateOptimizedToQuery($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + /** @dataProvider provideVerbosityInformation */ + public function testAggregateOptimizedToQuery($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { - if (version_compare($this->getServerVersion(), '4.2.0', '<')) { - $this->markTestSkipped('MongoDB < 4.2 does not optimize simple aggregation pipelines'); - } + $this->skipIfServerVersion('<', '4.2.0', 'MongoDB < 4.2 does not optimize simple aggregation pipelines'); $this->createFixtures(3); @@ -434,7 +366,7 @@ public function provideVerbosityInformation() ]; } - private function assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected, $stagesExpected = false) + private function assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected, $stagesExpected = false): void { if ($stagesExpected) { $this->assertArrayHasKey('stages', $result); @@ -456,10 +388,8 @@ private function assertExplainResult($result, $executionStatsExpected, $allPlans /** * Create data fixtures. - * - * @param integer $n */ - private function createFixtures($n) + private function createFixtures(int $n): void { $bulkWrite = new BulkWrite(['ordered' => true]); diff --git a/tests/Operation/ExplainTest.php b/tests/Operation/ExplainTest.php index 945e300a4..a77f39129 100644 --- a/tests/Operation/ExplainTest.php +++ b/tests/Operation/ExplainTest.php @@ -8,10 +8,8 @@ class ExplainTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $explainable = $this->getMockBuilder(Explainable::class)->getMock(); $this->expectException(InvalidArgumentException::class); diff --git a/tests/Operation/FindAndModifyFunctionalTest.php b/tests/Operation/FindAndModifyFunctionalTest.php index d402f4717..ebdbd1092 100644 --- a/tests/Operation/FindAndModifyFunctionalTest.php +++ b/tests/Operation/FindAndModifyFunctionalTest.php @@ -2,26 +2,97 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; use MongoDB\Driver\BulkWrite; -use MongoDB\Driver\Manager; -use MongoDB\Driver\ReadPreference; +use MongoDB\Driver\Exception\CommandException; +use MongoDB\Driver\WriteConcern; +use MongoDB\Exception\UnsupportedException; use MongoDB\Model\BSONDocument; use MongoDB\Operation\FindAndModify; use MongoDB\Tests\CommandObserver; -use function version_compare; +use stdClass; class FindAndModifyFunctionalTest extends FunctionalTestCase { + /** @dataProvider provideQueryDocuments */ + public function testQueryDocuments($query, stdClass $expectedQuery): void + { + (new CommandObserver())->observe( + function () use ($query): void { + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + ['query' => $query, 'remove' => true] + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->query ?? null); + } + ); + } + + public function provideQueryDocuments(): array + { + $expected = (object) ['x' => 1]; + + return [ + 'array' => [['x' => 1], $expected], + 'object' => [(object) ['x' => 1], $expected], + 'Serializable' => [new BSONDocument(['x' => 1]), $expected], + 'Document' => [Document::fromPHP(['x' => 1]), $expected], + ]; + } + /** - * @see https://jira.mongodb.org/browse/PHPLIB-344 + * @dataProvider provideReplacementDocuments + * @dataProvider provideUpdateDocuments + * @dataProvider provideUpdatePipelines + * @dataProvider provideReplacementDocumentLikePipeline */ - public function testManagerReadConcernIsOmitted() + public function testUpdateDocuments($update, $expectedUpdate): void { - $manager = new Manager(static::getUri(), ['readConcernLevel' => 'majority']); - $server = $manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY)); + (new CommandObserver())->observe( + function () use ($update): void { + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + [ + 'query' => ['x' => 1], + 'update' => $update, + ] + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedUpdate): void { + $this->assertEquals($expectedUpdate, $event['started']->getCommand()->update ?? null); + } + ); + } + + public function provideReplacementDocumentLikePipeline(): array + { + /* Note: this expected value differs from UpdateFunctionalTest because + * FindAndModify is not affected by libmongoc's pipeline detection for + * update commands (see: CDRIVER-4658). */ + return [ + 'replacement_like_pipeline' => [ + (object) ['0' => ['$set' => ['x' => 1]]], + (object) ['0' => (object) ['$set' => (object) ['x' => 1]]], + ], + ]; + } + + /** @see https://jira.mongodb.org/browse/PHPLIB-344 */ + public function testManagerReadConcernIsOmitted(): void + { + $manager = static::createTestManager(null, ['readConcernLevel' => 'majority']); + $server = $manager->selectServer(); (new CommandObserver())->observe( - function () use ($server) { + function () use ($server): void { $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), @@ -30,16 +101,16 @@ function () use ($server) { $operation->execute($server); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); } ); } - public function testDefaultWriteConcernIsOmitted() + public function testDefaultWriteConcernIsOmitted(): void { (new CommandObserver())->observe( - function () { + function (): void { $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), @@ -48,20 +119,67 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); } ); } - public function testSessionOption() + public function testHintOptionUnsupportedClientSideError(): void + { + $this->skipIfServerVersion('>=', '4.2.0', 'server reports error for unsupported findAndModify options'); + + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + ['remove' => true, 'hint' => '_id_'] + ); + + $this->expectException(UnsupportedException::class); + $this->expectExceptionMessage('Hint is not supported by the server executing this operation'); + + $operation->execute($this->getPrimaryServer()); + } + + public function testHintOptionAndUnacknowledgedWriteConcernUnsupportedClientSideError(): void + { + $this->skipIfServerVersion('>=', '4.4.0', 'hint is supported'); + + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + ['remove' => true, 'hint' => '_id_', 'writeConcern' => new WriteConcern(0)] + ); + + $this->expectException(UnsupportedException::class); + $this->expectExceptionMessage('Hint is not supported by the server executing this operation'); + + $operation->execute($this->getPrimaryServer()); + } + + public function testFindAndModifyReportedWriteConcernError(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); + if (($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets()) || ! $this->isReplicaSet()) { + $this->markTestSkipped('Test only applies to replica sets'); } + $this->expectException(CommandException::class); + $this->expectExceptionCode(100 /* UnsatisfiableWriteConcern */); + $this->expectExceptionMessageMatches('/Write Concern error:/'); + + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + ['remove' => true, 'writeConcern' => new WriteConcern(50)] + ); + + $operation->execute($this->getPrimaryServer()); + } + + public function testSessionOption(): void + { (new CommandObserver())->observe( - function () { + function (): void { $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), @@ -70,20 +188,16 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); } - public function testBypassDocumentValidationSetWhenTrue() + public function testBypassDocumentValidationSetWhenTrue(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), @@ -92,21 +206,17 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); } ); } - public function testBypassDocumentValidationUnsetWhenFalse() + public function testBypassDocumentValidationUnsetWhenFalse(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), @@ -115,16 +225,14 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); } ); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocument - */ - public function testTypeMapOption(array $typeMap = null, $expectedDocument) + /** @dataProvider provideTypeMapOptionsAndExpectedDocument */ + public function testTypeMapOption(?array $typeMap, $expectedDocument): void { $this->createFixtures(1); @@ -173,10 +281,8 @@ public function provideTypeMapOptionsAndExpectedDocument() /** * Create data fixtures. - * - * @param integer $n */ - private function createFixtures($n) + private function createFixtures(int $n): void { $bulkWrite = new BulkWrite(['ordered' => true]); diff --git a/tests/Operation/FindAndModifyTest.php b/tests/Operation/FindAndModifyTest.php index 7f5cb99cc..a0738bb12 100644 --- a/tests/Operation/FindAndModifyTest.php +++ b/tests/Operation/FindAndModifyTest.php @@ -2,15 +2,14 @@ namespace MongoDB\Tests\Operation; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\FindAndModify; class FindAndModifyTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), $options); @@ -79,10 +78,52 @@ public function provideInvalidConstructorOptions() return $options; } - public function testConstructorUpdateAndRemoveOptionsAreMutuallyExclusive() + public function testConstructorUpdateAndRemoveOptionsAreMutuallyExclusive(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('The "remove" option must be true or an "update" document must be specified, but not both'); new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), ['remove' => true, 'update' => []]); } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'arrayFilters' => [['x' => 1]], + 'bypassDocumentValidation' => true, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'fields' => ['_id' => 0], + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'new' => true, + 'query' => ['y' => 2], + 'sort' => ['x' => 1], + 'update' => ['$set' => ['x' => 2]], + 'upsert' => true, + 'let' => ['a' => 3], + // Intentionally omitted options + 'remove' => false, // When "update" is set + 'typeMap' => ['root' => 'array'], + 'writeConcern' => new WriteConcern(0), + ]; + $operation = new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), $options); + + $expected = [ + 'findAndModify' => $this->getCollectionName(), + 'new' => true, + 'upsert' => true, + 'collation' => (object) ['locale' => 'fr'], + 'fields' => (object) ['_id' => 0], + 'let' => (object) ['a' => 3], + 'query' => (object) ['y' => 2], + 'sort' => (object) ['x' => 1], + 'update' => (object) ['$set' => ['x' => 2]], + 'arrayFilters' => [['x' => 1]], + 'bypassDocumentValidation' => true, + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); + } } diff --git a/tests/Operation/FindFunctionalTest.php b/tests/Operation/FindFunctionalTest.php index 1aff69e50..a7975f338 100644 --- a/tests/Operation/FindFunctionalTest.php +++ b/tests/Operation/FindFunctionalTest.php @@ -2,22 +2,74 @@ namespace MongoDB\Tests\Operation; -use IteratorIterator; +use MongoDB\BSON\Document; use MongoDB\Driver\BulkWrite; use MongoDB\Driver\ReadPreference; -use MongoDB\Operation\CreateCollection; +use MongoDB\Model\BSONDocument; use MongoDB\Operation\CreateIndexes; use MongoDB\Operation\Find; use MongoDB\Tests\CommandObserver; +use stdClass; + use function microtime; -use function version_compare; class FindFunctionalTest extends FunctionalTestCase { - public function testDefaultReadConcernIsOmitted() + /** @dataProvider provideFilterDocuments */ + public function testFilterDocuments($filter, stdClass $expectedQuery): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new Find( + $this->getDatabaseName(), + $this->getCollectionName(), + $filter + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->filter ?? null); + } + ); + } + + /** @dataProvider provideModifierDocuments */ + public function testModifierDocuments($modifiers, stdClass $expectedSort): void + { + (new CommandObserver())->observe( + function () use ($modifiers): void { + $operation = new Find( + $this->getDatabaseName(), + $this->getCollectionName(), + [], + ['modifiers' => $modifiers] + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedSort): void { + $this->assertEquals($expectedSort, $event['started']->getCommand()->sort ?? null); + } + ); + } + + public function provideModifierDocuments(): array + { + $expectedSort = (object) ['x' => 1]; + + return [ + 'array' => [['$orderby' => ['x' => 1]], $expectedSort], + 'object' => [(object) ['$orderby' => ['x' => 1]], $expectedSort], + 'Serializable' => [new BSONDocument(['$orderby' => ['x' => 1]]), $expectedSort], + 'Document' => [Document::fromPHP(['$orderby' => ['x' => 1]]), $expectedSort], + ]; + } + + public function testDefaultReadConcernIsOmitted(): void { (new CommandObserver())->observe( - function () { + function (): void { $operation = new Find( $this->getDatabaseName(), $this->getCollectionName(), @@ -27,13 +79,13 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); } ); } - public function testHintOption() + public function testHintOption(): void { $bulkWrite = new BulkWrite(); $bulkWrite->insert(['_id' => 1, 'x' => 1]); @@ -84,14 +136,10 @@ public function testHintOption() } } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new Find( $this->getDatabaseName(), $this->getCollectionName(), @@ -101,16 +149,14 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ - public function testTypeMapOption(array $typeMap, array $expectedDocuments) + /** @dataProvider provideTypeMapOptionsAndExpectedDocuments */ + public function testTypeMapOption(array $typeMap, array $expectedDocuments): void { $this->createFixtures(3); @@ -150,12 +196,8 @@ public function provideTypeMapOptionsAndExpectedDocuments() ]; } - public function testMaxAwaitTimeMS() + public function testMaxAwaitTimeMS(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('maxAwaitTimeMS option is not supported'); - } - $maxAwaitTimeMS = 100; /* Calculate an approximate pivot to use for time assertions. We will @@ -172,8 +214,7 @@ public function testMaxAwaitTimeMS() 'size' => 1048576, ]; - $operation = new CreateCollection($databaseName, $cappedCollectionName, $cappedCollectionOptions); - $operation->execute($this->getPrimaryServer()); + $this->createCollection($databaseName, $cappedCollectionName, $cappedCollectionOptions); // Insert documents into the capped collection. $bulkWrite = new BulkWrite(['ordered' => true]); @@ -183,27 +224,26 @@ public function testMaxAwaitTimeMS() $operation = new Find($databaseName, $cappedCollectionName, [], ['cursorType' => Find::TAILABLE_AWAIT, 'maxAwaitTimeMS' => $maxAwaitTimeMS]); $cursor = $operation->execute($this->getPrimaryServer()); - $it = new IteratorIterator($cursor); /* The initial query includes the one and only document in its result * batch, so we should not expect a delay. */ $startTime = microtime(true); - $it->rewind(); + $cursor->rewind(); $duration = microtime(true) - $startTime; $this->assertLessThan($pivot, $duration); - $this->assertTrue($it->valid()); - $this->assertSameDocument(['_id' => 1], $it->current()); + $this->assertTrue($cursor->valid()); + $this->assertSameDocument(['_id' => 1], $cursor->current()); /* Advancing again takes us to the last document of the result batch, * but still should not issue a getMore */ $startTime = microtime(true); - $it->next(); + $cursor->next(); $duration = microtime(true) - $startTime; $this->assertLessThan($pivot, $duration); - $this->assertTrue($it->valid()); - $this->assertSameDocument(['_id' => 2], $it->current()); + $this->assertTrue($cursor->valid()); + $this->assertSameDocument(['_id' => 2], $cursor->current()); /* Now that we've reached the end of the initial result batch, advancing * again will issue a getMore. Expect to wait at least maxAwaitTimeMS, @@ -211,20 +251,20 @@ public function testMaxAwaitTimeMS() * query thread. Also ensure we don't wait too long (server default is * one second). */ $startTime = microtime(true); - $it->next(); + $cursor->next(); $duration = microtime(true) - $startTime; $this->assertGreaterThan($pivot, $duration); $this->assertLessThan(0.5, $duration); - $this->assertFalse($it->valid()); + $this->assertFalse($cursor->valid()); } - public function testReadPreferenceWithinTransaction() + public function testReadPreferenceWithinTransaction(): void { $this->skipIfTransactionsAreNotSupported(); // Collection must be created before the transaction starts - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -234,7 +274,7 @@ public function testReadPreferenceWithinTransaction() $filter = ['_id' => ['$lt' => 3]]; $options = [ - 'readPreference' => new ReadPreference('primary'), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), 'session' => $session, ]; @@ -256,11 +296,8 @@ public function testReadPreferenceWithinTransaction() /** * Create data fixtures. - * - * @param integer $n - * @param array $executeBulkWriteOptions */ - private function createFixtures($n, array $executeBulkWriteOptions = []) + private function createFixtures(int $n, array $executeBulkWriteOptions = []): void { $bulkWrite = new BulkWrite(['ordered' => true]); diff --git a/tests/Operation/FindOneAndDeleteTest.php b/tests/Operation/FindOneAndDeleteTest.php index 306ec3568..e63062ace 100644 --- a/tests/Operation/FindOneAndDeleteTest.php +++ b/tests/Operation/FindOneAndDeleteTest.php @@ -2,24 +2,21 @@ namespace MongoDB\Tests\Operation; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\FindOneAndDelete; class FindOneAndDeleteTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), $filter); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), [], $options); @@ -35,4 +32,35 @@ public function provideInvalidConstructorOptions() return $options; } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'sort' => ['x' => 1], + 'let' => ['a' => 3], + // Intentionally omitted options + 'projection' => ['_id' => 0], + 'typeMap' => ['root' => 'array'], + 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), + ]; + $operation = new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), ['y' => 2], $options); + + $expected = [ + 'findAndModify' => $this->getCollectionName(), + 'collation' => (object) ['locale' => 'fr'], + 'fields' => (object) ['_id' => 0], + 'let' => (object) ['a' => 3], + 'query' => (object) ['y' => 2], + 'sort' => (object) ['x' => 1], + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'remove' => true, + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); + } } diff --git a/tests/Operation/FindOneAndReplaceTest.php b/tests/Operation/FindOneAndReplaceTest.php index 974d413a3..743098e4f 100644 --- a/tests/Operation/FindOneAndReplaceTest.php +++ b/tests/Operation/FindOneAndReplaceTest.php @@ -2,40 +2,56 @@ namespace MongoDB\Tests\Operation; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\FindOneAndReplace; class FindOneAndReplaceTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), $filter, []); } + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorReplacementArgumentTypeCheck($replacement): void + { + $this->expectException(InvalidArgumentException::class); + new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], $replacement); + } + /** - * @dataProvider provideInvalidDocumentValues + * @dataProvider provideReplacementDocuments + * @doesNotPerformAssertions */ - public function testConstructorReplacementArgumentTypeCheck($replacement) + public function testConstructorReplacementArgument($replacement): void { - $this->expectException(InvalidArgumentException::class); new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], $replacement); } - public function testConstructorReplacementArgumentRequiresNoOperators() + /** @dataProvider provideUpdateDocuments */ + public function testConstructorReplacementArgumentProhibitsUpdateDocument($replacement): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('First key in $replacement argument is an update operator'); - new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], ['$set' => ['x' => 1]]); + $this->expectExceptionMessage('First key in $replacement is an update operator'); + new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], $replacement); } /** - * @dataProvider provideInvalidConstructorOptions + * @dataProvider provideUpdatePipelines + * @dataProvider provideEmptyUpdatePipelinesExcludingArray */ - public function testConstructorOptionTypeChecks(array $options) + public function testConstructorReplacementArgumentProhibitsUpdatePipeline($replacement): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$replacement is an update pipeline'); + new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], $replacement); + } + + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], [], $options); @@ -56,10 +72,8 @@ public function provideInvalidConstructorOptions() return $options; } - /** - * @dataProvider provideInvalidConstructorReturnDocumentOptions - */ - public function testConstructorReturnDocumentOption($returnDocument) + /** @dataProvider provideInvalidConstructorReturnDocumentOptions */ + public function testConstructorReturnDocumentOption($returnDocument): void { $this->expectException(InvalidArgumentException::class); new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], [], ['returnDocument' => $returnDocument]); @@ -69,4 +83,40 @@ public function provideInvalidConstructorReturnDocumentOptions() { return $this->wrapValuesForDataProvider([-1, 0, 3]); } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'bypassDocumentValidation' => true, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'fields' => ['_id' => 0], + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'projection' => ['_id' => 0], + 'sort' => ['x' => 1], + 'let' => ['a' => 3], + // Intentionally omitted options + 'returnDocument' => FindOneAndReplace::RETURN_DOCUMENT_AFTER, + 'typeMap' => ['root' => 'array'], + 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), + ]; + $operation = new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), ['y' => 2], ['y' => 3], $options); + + $expected = [ + 'findAndModify' => $this->getCollectionName(), + 'new' => true, + 'collation' => (object) ['locale' => 'fr'], + 'fields' => (object) ['_id' => 0], + 'let' => (object) ['a' => 3], + 'query' => (object) ['y' => 2], + 'sort' => (object) ['x' => 1], + 'update' => (object) ['y' => 3], + 'bypassDocumentValidation' => true, + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); + } } diff --git a/tests/Operation/FindOneAndUpdateTest.php b/tests/Operation/FindOneAndUpdateTest.php index 1391b7623..d174b73f3 100644 --- a/tests/Operation/FindOneAndUpdateTest.php +++ b/tests/Operation/FindOneAndUpdateTest.php @@ -2,40 +2,39 @@ namespace MongoDB\Tests\Operation; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\FindOneAndUpdate; class FindOneAndUpdateTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), $filter, []); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorUpdateArgumentTypeCheck($update) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorUpdateArgumentTypeCheck($update): void { $this->expectException(InvalidArgumentException::class); new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], $update); } - public function testConstructorUpdateArgumentRequiresOperatorsOrPipeline() + /** + * @dataProvider provideReplacementDocuments + * @dataProvider provideEmptyUpdatePipelines + */ + public function testConstructorUpdateArgumentProhibitsReplacementDocumentOrEmptyPipeline($update): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Expected an update document with operator as first key or a pipeline'); - new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], []); + $this->expectExceptionMessage('Expected update operator(s) or non-empty pipeline for $update'); + new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], $update); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], ['$set' => ['x' => 1]], $options); @@ -56,10 +55,8 @@ public function provideInvalidConstructorOptions() return $options; } - /** - * @dataProvider provideInvalidConstructorReturnDocumentOptions - */ - public function testConstructorReturnDocumentOption($returnDocument) + /** @dataProvider provideInvalidConstructorReturnDocumentOptions */ + public function testConstructorReturnDocumentOption($returnDocument): void { $this->expectException(InvalidArgumentException::class); new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], [], ['returnDocument' => $returnDocument]); @@ -69,4 +66,43 @@ public function provideInvalidConstructorReturnDocumentOptions() { return $this->wrapValuesForDataProvider([-1, 0, 3]); } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'arrayFilters' => [['x' => 1]], + 'bypassDocumentValidation' => true, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'sort' => ['x' => 1], + 'upsert' => true, + 'let' => ['a' => 3], + // Intentionally omitted options + 'projection' => ['_id' => 0], + 'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER, + 'typeMap' => ['root' => 'array'], + 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), + ]; + $operation = new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), ['y' => 2], ['$set' => ['x' => 2]], $options); + + $expected = [ + 'findAndModify' => $this->getCollectionName(), + 'new' => true, + 'upsert' => true, + 'collation' => (object) ['locale' => 'fr'], + 'fields' => (object) ['_id' => 0], + 'let' => (object) ['a' => 3], + 'query' => (object) ['y' => 2], + 'sort' => (object) ['x' => 1], + 'update' => (object) ['$set' => ['x' => 2]], + 'arrayFilters' => [['x' => 1]], + 'bypassDocumentValidation' => true, + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); + } } diff --git a/tests/Operation/FindOneFunctionalTest.php b/tests/Operation/FindOneFunctionalTest.php index 62fac8287..d18d056e3 100644 --- a/tests/Operation/FindOneFunctionalTest.php +++ b/tests/Operation/FindOneFunctionalTest.php @@ -7,10 +7,8 @@ class FindOneFunctionalTest extends FunctionalTestCase { - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocument - */ - public function testTypeMapOption(array $typeMap, $expectedDocument) + /** @dataProvider provideTypeMapOptionsAndExpectedDocument */ + public function testTypeMapOption(array $typeMap, $expectedDocument): void { $this->createFixtures(1); @@ -40,10 +38,8 @@ public function provideTypeMapOptionsAndExpectedDocument() /** * Create data fixtures. - * - * @param integer $n */ - private function createFixtures($n) + private function createFixtures(int $n): void { $bulkWrite = new BulkWrite(['ordered' => true]); diff --git a/tests/Operation/FindTest.php b/tests/Operation/FindTest.php index 4464ea5a8..1da7d23fa 100644 --- a/tests/Operation/FindTest.php +++ b/tests/Operation/FindTest.php @@ -2,24 +2,22 @@ namespace MongoDB\Tests\Operation; +use MongoDB\Driver\ReadConcern; +use MongoDB\Driver\ReadPreference; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Find; class FindTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); new Find($this->getDatabaseName(), $this->getCollectionName(), $filter); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Find($this->getDatabaseName(), $this->getCollectionName(), [], $options); @@ -41,10 +39,6 @@ public function provideInvalidConstructorOptions() $options[][] = ['collation' => $value]; } - foreach ($this->getInvalidStringValues() as $value) { - $options[][] = ['comment' => $value]; - } - foreach ($this->getInvalidIntegerValues() as $value) { $options[][] = ['cursorType' => $value]; } @@ -128,20 +122,20 @@ public function provideInvalidConstructorOptions() return $options; } - public function testSnapshotOptionIsDeprecated() + public function testSnapshotOptionIsDeprecated(): void { - $this->assertDeprecated(function () { + $this->assertDeprecated(function (): void { new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['snapshot' => true]); }); - $this->assertDeprecated(function () { + $this->assertDeprecated(function (): void { new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['snapshot' => false]); }); } - public function testMaxScanOptionIsDeprecated() + public function testMaxScanOptionIsDeprecated(): void { - $this->assertDeprecated(function () { + $this->assertDeprecated(function (): void { new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['maxScan' => 1]); }); } @@ -151,10 +145,8 @@ private function getInvalidHintValues() return [123, 3.14, true]; } - /** - * @dataProvider provideInvalidConstructorCursorTypeOptions - */ - public function testConstructorCursorTypeOption($cursorType) + /** @dataProvider provideInvalidConstructorCursorTypeOptions */ + public function testConstructorCursorTypeOption($cursorType): void { $this->expectException(InvalidArgumentException::class); new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['cursorType' => $cursorType]); @@ -164,4 +156,62 @@ public function provideInvalidConstructorCursorTypeOptions() { return $this->wrapValuesForDataProvider([-1, 0, 4]); } + + public function testExplainableCommandDocument(): void + { + // all options except deprecated "snapshot" and "maxScan" + $options = [ + 'allowDiskUse' => true, + 'allowPartialResults' => true, + 'batchSize' => 123, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'limit' => 15, + 'max' => ['x' => 100], + 'maxTimeMS' => 100, + 'min' => ['x' => 10], + 'noCursorTimeout' => true, + 'oplogReplay' => true, + 'projection' => ['_id' => 0], + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + 'returnKey' => true, + 'showRecordId' => true, + 'skip' => 5, + 'sort' => ['x' => 1], + 'let' => ['y' => 2], + // Intentionally omitted options + 'cursorType' => Find::NON_TAILABLE, + 'maxAwaitTimeMS' => 500, + 'modifiers' => ['foo' => 'bar'], + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), + 'typeMap' => ['root' => 'array'], + ]; + $operation = new Find($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $options); + + $expected = [ + 'find' => $this->getCollectionName(), + 'filter' => (object) ['x' => 1], + 'allowDiskUse' => true, + 'allowPartialResults' => true, + 'batchSize' => 123, + 'comment' => 'explain me', + 'hint' => '_id_', + 'limit' => 15, + 'maxTimeMS' => 100, + 'noCursorTimeout' => true, + 'oplogReplay' => true, + 'projection' => ['_id' => 0], + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + 'returnKey' => true, + 'showRecordId' => true, + 'skip' => 5, + 'sort' => ['x' => 1], + 'collation' => (object) ['locale' => 'fr'], + 'let' => (object) ['y' => 2], + 'max' => (object) ['x' => 100], + 'min' => (object) ['x' => 10], + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); + } } diff --git a/tests/Operation/FunctionalTestCase.php b/tests/Operation/FunctionalTestCase.php index ef4e76b7c..c07a35712 100644 --- a/tests/Operation/FunctionalTestCase.php +++ b/tests/Operation/FunctionalTestCase.php @@ -2,34 +2,82 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; +use MongoDB\BSON\PackedArray; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\WriteConcern; +use MongoDB\Model\BSONArray; +use MongoDB\Model\BSONDocument; use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; /** * Base class for Operation functional tests. */ abstract class FunctionalTestCase extends BaseFunctionalTestCase { - use SetUpTearDownTrait; - - private function doSetUp() + public function setUp(): void { parent::setUp(); - $this->dropCollection(); + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + } + + public function provideFilterDocuments(): array + { + $expected = (object) ['x' => 1]; + + return [ + 'filter:array' => [['x' => 1], $expected], + 'filter:object' => [(object) ['x' => 1], $expected], + 'filter:Serializable' => [new BSONDocument(['x' => 1]), $expected], + 'filter:Document' => [Document::fromPHP(['x' => 1]), $expected], + ]; + } + + public function provideReplacementDocuments(): array + { + $expected = (object) ['x' => 1]; + $expectedEmpty = (object) []; + + return [ + 'replacement:array' => [['x' => 1], $expected], + 'replacement:object' => [(object) ['x' => 1], $expected], + 'replacement:Serializable' => [new BSONDocument(['x' => 1]), $expected], + 'replacement:Document' => [Document::fromPHP(['x' => 1]), $expected], + /* Note: empty arrays could also express an empty pipeline, but we + * should interpret them as an empty replacement document for BC. */ + 'empty_replacement:array' => [[], $expectedEmpty], + 'empty_replacement:object' => [(object) [], $expectedEmpty], + 'empty_replacement:Serializable' => [new BSONDocument([]), $expectedEmpty], + 'empty_replacement:Document' => [Document::fromPHP([]), $expectedEmpty], + ]; } - private function doTearDown() + public function provideUpdateDocuments(): array { - if ($this->hasFailed()) { - return; - } + $expected = (object) ['$set' => (object) ['x' => 1]]; - $this->dropCollection(); + return [ + 'update:array' => [['$set' => ['x' => 1]], $expected], + 'update:object' => [(object) ['$set' => ['x' => 1]], $expected], + 'update:Serializable' => [new BSONDocument(['$set' => ['x' => 1]]), $expected], + 'update:Document' => [Document::fromPHP(['$set' => ['x' => 1]]), $expected], + ]; + } + + public function provideUpdatePipelines(): array + { + $expected = [(object) ['$set' => (object) ['x' => 1]]]; - parent::tearDown(); + return [ + 'pipeline:array' => [[['$set' => ['x' => 1]]], $expected], + 'pipeline:Serializable' => [new BSONArray([['$set' => ['x' => 1]]]), $expected], + 'pipeline:PackedArray' => [PackedArray::fromPHP([['$set' => ['x' => 1]]]), $expected], + /* Note: although empty pipelines are valid NOPs for update and + * findAndModify commands, they are not supported for updates in + * libmongoc since they are indistinguishable from empty replacement + * documents (both are empty bson_t structs). */ + ]; } protected function createDefaultReadConcern() diff --git a/tests/Operation/InsertManyFunctionalTest.php b/tests/Operation/InsertManyFunctionalTest.php index d8f105d64..4d47ea104 100644 --- a/tests/Operation/InsertManyFunctionalTest.php +++ b/tests/Operation/InsertManyFunctionalTest.php @@ -2,6 +2,7 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; use MongoDB\BSON\ObjectId; use MongoDB\Collection; use MongoDB\Driver\WriteConcern; @@ -10,62 +11,116 @@ use MongoDB\Model\BSONDocument; use MongoDB\Operation\InsertMany; use MongoDB\Tests\CommandObserver; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; -use function version_compare; class InsertManyFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; - /** @var Collection */ private $collection; - private function doSetUp() + public function setUp(): void { parent::setUp(); $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); } - public function testInsertMany() + public function testDocumentEncoding(): void { $documents = [ - ['_id' => 'foo', 'x' => 11], - ['x' => 22], - (object) ['_id' => 'bar', 'x' => 33], - new BSONDocument(['_id' => 'baz', 'x' => 44]), + ['_id' => 1], + (object) ['_id' => 2], + new BSONDocument(['_id' => 3]), + Document::fromPHP(['_id' => 4]), + ['x' => 1], + (object) ['x' => 2], + new BSONDocument(['x' => 3]), + Document::fromPHP(['x' => 4]), + ]; + + $expectedDocuments = [ + (object) ['_id' => 1], + (object) ['_id' => 2], + (object) ['_id' => 3], + (object) ['_id' => 4], + // Note: _id placeholders must be replaced with generated ObjectIds + (object) ['_id' => null, 'x' => 1], + (object) ['_id' => null, 'x' => 2], + (object) ['_id' => null, 'x' => 3], + (object) ['_id' => null, 'x' => 4], + ]; + + (new CommandObserver())->observe( + function () use ($documents, $expectedDocuments): void { + $operation = new InsertMany( + $this->getDatabaseName(), + $this->getCollectionName(), + $documents + ); + + $result = $operation->execute($this->getPrimaryServer()); + $insertedIds = $result->getInsertedIds(); + + foreach ($expectedDocuments as $i => $expectedDocument) { + // Replace _id placeholder if necessary + if ($expectedDocument->_id === null) { + $expectedDocument->_id = $insertedIds[$i]; + } + } + }, + function (array $event) use ($expectedDocuments): void { + $this->assertEquals($expectedDocuments, $event['started']->getCommand()->documents ?? null); + } + ); + } + + public function testInsertMany(): void + { + $documents = [ + ['_id' => 1], + (object) ['_id' => 2], + new BSONDocument(['_id' => 3]), + Document::fromPHP(['_id' => 4]), + ['x' => 1], + (object) ['x' => 2], + new BSONDocument(['x' => 3]), + Document::fromPHP(['x' => 4]), ]; $operation = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), $documents); $result = $operation->execute($this->getPrimaryServer()); $this->assertInstanceOf(InsertManyResult::class, $result); - $this->assertSame(4, $result->getInsertedCount()); + $this->assertSame(8, $result->getInsertedCount()); $insertedIds = $result->getInsertedIds(); - $this->assertSame('foo', $insertedIds[0]); - $this->assertInstanceOf(ObjectId::class, $insertedIds[1]); - $this->assertSame('bar', $insertedIds[2]); - $this->assertSame('baz', $insertedIds[3]); + $this->assertSame(1, $insertedIds[0]); + $this->assertSame(2, $insertedIds[1]); + $this->assertSame(3, $insertedIds[2]); + $this->assertSame(4, $insertedIds[3]); + $this->assertInstanceOf(ObjectId::class, $insertedIds[4]); + $this->assertInstanceOf(ObjectId::class, $insertedIds[5]); + $this->assertInstanceOf(ObjectId::class, $insertedIds[6]); + $this->assertInstanceOf(ObjectId::class, $insertedIds[7]); $expected = [ - ['_id' => 'foo', 'x' => 11], - ['_id' => $insertedIds[1], 'x' => 22], - ['_id' => 'bar', 'x' => 33], - ['_id' => 'baz', 'x' => 44], + ['_id' => 1], + ['_id' => 2], + ['_id' => 3], + ['_id' => 4], + ['_id' => $insertedIds[4], 'x' => 1], + ['_id' => $insertedIds[5], 'x' => 2], + ['_id' => $insertedIds[6], 'x' => 3], + ['_id' => $insertedIds[7], 'x' => 4], + ]; $this->assertSameDocuments($expected, $this->collection->find()); } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new InsertMany( $this->getDatabaseName(), $this->getCollectionName(), @@ -75,20 +130,16 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); } - public function testBypassDocumentValidationSetWhenTrue() + public function testBypassDocumentValidationSetWhenTrue(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new InsertMany( $this->getDatabaseName(), $this->getCollectionName(), @@ -98,21 +149,17 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); } ); } - public function testBypassDocumentValidationUnsetWhenFalse() + public function testBypassDocumentValidationUnsetWhenFalse(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new InsertMany( $this->getDatabaseName(), $this->getCollectionName(), @@ -122,7 +169,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); } ); @@ -141,20 +188,16 @@ public function testUnacknowledgedWriteConcern() return $result; } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesInsertedCount(InsertManyResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesInsertedCount(InsertManyResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); $result->getInsertedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesInsertedId(InsertManyResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesInsertedId(InsertManyResult $result): void { $this->assertInstanceOf(ObjectId::class, $result->getInsertedIds()[0]); } diff --git a/tests/Operation/InsertManyTest.php b/tests/Operation/InsertManyTest.php index 38955fd58..67d91418e 100644 --- a/tests/Operation/InsertManyTest.php +++ b/tests/Operation/InsertManyTest.php @@ -7,34 +7,30 @@ class InsertManyTest extends TestCase { - public function testConstructorDocumentsMustNotBeEmpty() + public function testConstructorDocumentsMustNotBeEmpty(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$documents is empty'); new InsertMany($this->getDatabaseName(), $this->getCollectionName(), []); } - public function testConstructorDocumentsMustBeAList() + public function testConstructorDocumentsMustBeAList(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$documents is not a list (unexpected index: "1")'); + $this->expectExceptionMessage('$documents is not a list'); new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [1 => ['x' => 1]]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorDocumentsArgumentElementTypeChecks($document) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorDocumentsArgumentElementTypeChecks($document): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$documents[0\] to have type "array or object" but found "[\w ]+"/'); new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [$document]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [['x' => 1]], $options); diff --git a/tests/Operation/InsertOneFunctionalTest.php b/tests/Operation/InsertOneFunctionalTest.php index b0eefded9..61a517894 100644 --- a/tests/Operation/InsertOneFunctionalTest.php +++ b/tests/Operation/InsertOneFunctionalTest.php @@ -2,6 +2,7 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; use MongoDB\BSON\ObjectId; use MongoDB\Collection; use MongoDB\Driver\WriteConcern; @@ -10,17 +11,14 @@ use MongoDB\Model\BSONDocument; use MongoDB\Operation\InsertOne; use MongoDB\Tests\CommandObserver; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; -use function version_compare; +use stdClass; class InsertOneFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; - /** @var Collection */ private $collection; - private function doSetUp() + public function setUp(): void { parent::setUp(); @@ -28,37 +26,75 @@ private function doSetUp() } /** - * @dataProvider provideDocumentWithExistingId + * @dataProvider provideDocumentsWithIds + * @dataProvider provideDocumentsWithoutIds */ - public function testInsertOneWithExistingId($document) + public function testDocumentEncoding($document, stdClass $expectedDocument): void { - $operation = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), $document); - $result = $operation->execute($this->getPrimaryServer()); + (new CommandObserver())->observe( + function () use ($document, $expectedDocument): void { + $operation = new InsertOne( + $this->getDatabaseName(), + $this->getCollectionName(), + $document + ); - $this->assertInstanceOf(InsertOneResult::class, $result); - $this->assertSame(1, $result->getInsertedCount()); - $this->assertSame('foo', $result->getInsertedId()); + $result = $operation->execute($this->getPrimaryServer()); - $expected = [ - ['_id' => 'foo', 'x' => 11], - ]; + // Replace _id placeholder if necessary + if ($expectedDocument->_id === null) { + $expectedDocument->_id = $result->getInsertedId(); + } + }, + function (array $event) use ($expectedDocument): void { + $this->assertEquals($expectedDocument, $event['started']->getCommand()->documents[0] ?? null); + } + ); + } - $this->assertSameDocuments($expected, $this->collection->find()); + public function provideDocumentsWithIds(): array + { + $expectedDocument = (object) ['_id' => 1]; + + return [ + 'with_id:array' => [['_id' => 1], $expectedDocument], + 'with_id:object' => [(object) ['_id' => 1], $expectedDocument], + 'with_id:Serializable' => [new BSONDocument(['_id' => 1]), $expectedDocument], + 'with_id:Document' => [Document::fromPHP(['_id' => 1]), $expectedDocument], + ]; } - public function provideDocumentWithExistingId() + public function provideDocumentsWithoutIds(): array { + /* Note: _id placeholders must be replaced with generated ObjectIds. We + * also clone the value for each data set since tests may need to modify + * the object. */ + $expectedDocument = (object) ['_id' => null, 'x' => 1]; + return [ - [['_id' => 'foo', 'x' => 11]], - [(object) ['_id' => 'foo', 'x' => 11]], - [new BSONDocument(['_id' => 'foo', 'x' => 11])], + 'without_id:array' => [['x' => 1], clone $expectedDocument], + 'without_id:object' => [(object) ['x' => 1], clone $expectedDocument], + 'without_id:Serializable' => [new BSONDocument(['x' => 1]), clone $expectedDocument], + 'without_id:Document' => [Document::fromPHP(['x' => 1]), clone $expectedDocument], ]; } - public function testInsertOneWithGeneratedId() + /** @dataProvider provideDocumentsWithIds */ + public function testInsertOneWithExistingId($document, stdClass $expectedDocument): void { - $document = ['x' => 11]; + $operation = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), $document); + $result = $operation->execute($this->getPrimaryServer()); + + $this->assertInstanceOf(InsertOneResult::class, $result); + $this->assertSame(1, $result->getInsertedCount()); + $this->assertSame($expectedDocument->_id, $result->getInsertedId()); + + $this->assertSameDocuments([$expectedDocument], $this->collection->find()); + } + /** @dataProvider provideDocumentsWithoutIds */ + public function testInsertOneWithGeneratedId($document, stdClass $expectedDocument): void + { $operation = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), $document); $result = $operation->execute($this->getPrimaryServer()); @@ -66,21 +102,16 @@ public function testInsertOneWithGeneratedId() $this->assertSame(1, $result->getInsertedCount()); $this->assertInstanceOf(ObjectId::class, $result->getInsertedId()); - $expected = [ - ['_id' => $result->getInsertedId(), 'x' => 11], - ]; + // Replace _id placeholder + $expectedDocument->_id = $result->getInsertedId(); - $this->assertSameDocuments($expected, $this->collection->find()); + $this->assertSameDocuments([$expectedDocument], $this->collection->find()); } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new InsertOne( $this->getDatabaseName(), $this->getCollectionName(), @@ -90,20 +121,16 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); } - public function testBypassDocumentValidationSetWhenTrue() + public function testBypassDocumentValidationSetWhenTrue(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new InsertOne( $this->getDatabaseName(), $this->getCollectionName(), @@ -113,21 +140,17 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); } ); } - public function testBypassDocumentValidationUnsetWhenFalse() + public function testBypassDocumentValidationUnsetWhenFalse(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new InsertOne( $this->getDatabaseName(), $this->getCollectionName(), @@ -137,7 +160,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); } ); @@ -156,20 +179,16 @@ public function testUnacknowledgedWriteConcern() return $result; } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesInsertedCount(InsertOneResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesInsertedCount(InsertOneResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); $result->getInsertedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesInsertedId(InsertOneResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesInsertedId(InsertOneResult $result): void { $this->assertInstanceOf(ObjectId::class, $result->getInsertedId()); } diff --git a/tests/Operation/InsertOneTest.php b/tests/Operation/InsertOneTest.php index 2a91f8cc0..c51faea85 100644 --- a/tests/Operation/InsertOneTest.php +++ b/tests/Operation/InsertOneTest.php @@ -7,19 +7,15 @@ class InsertOneTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorDocumentArgumentTypeCheck($document) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorDocumentArgumentTypeCheck($document): void { $this->expectException(InvalidArgumentException::class); new InsertOne($this->getDatabaseName(), $this->getCollectionName(), $document); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $options); diff --git a/tests/Operation/ListCollectionNamesFunctionalTest.php b/tests/Operation/ListCollectionNamesFunctionalTest.php index dbb111ab7..e72c7789c 100644 --- a/tests/Operation/ListCollectionNamesFunctionalTest.php +++ b/tests/Operation/ListCollectionNamesFunctionalTest.php @@ -6,11 +6,10 @@ use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListCollectionNames; use MongoDB\Tests\CommandObserver; -use function version_compare; class ListCollectionNamesFunctionalTest extends FunctionalTestCase { - public function testListCollectionNamesForNewlyCreatedDatabase() + public function testListCollectionNamesForNewlyCreatedDatabase(): void { $server = $this->getPrimaryServer(); @@ -30,14 +29,28 @@ public function testListCollectionNamesForNewlyCreatedDatabase() } } - public function testSessionOption() + public function testAuthorizedCollectionsOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } + (new CommandObserver())->observe( + function (): void { + $operation = new ListCollectionNames( + $this->getDatabaseName(), + ['authorizedCollections' => true] + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event): void { + $this->assertObjectHasAttribute('authorizedCollections', $event['started']->getCommand()); + $this->assertSame(true, $event['started']->getCommand()->authorizedCollections); + } + ); + } + public function testSessionOption(): void + { (new CommandObserver())->observe( - function () { + function (): void { $operation = new ListCollectionNames( $this->getDatabaseName(), ['session' => $this->createSession()] @@ -45,7 +58,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); diff --git a/tests/Operation/ListCollectionsFunctionalTest.php b/tests/Operation/ListCollectionsFunctionalTest.php index 5d3228da7..ec48e7e79 100644 --- a/tests/Operation/ListCollectionsFunctionalTest.php +++ b/tests/Operation/ListCollectionsFunctionalTest.php @@ -8,11 +8,10 @@ use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListCollections; use MongoDB\Tests\CommandObserver; -use function version_compare; class ListCollectionsFunctionalTest extends FunctionalTestCase { - public function testListCollectionsForNewlyCreatedDatabase() + public function testListCollectionsForNewlyCreatedDatabase(): void { $server = $this->getPrimaryServer(); @@ -36,12 +35,14 @@ public function testListCollectionsForNewlyCreatedDatabase() } } - public function testIdIndexAndInfo() + /** + * @group matrix-testing-exclude-server-4.4-driver-4.0 + * @group matrix-testing-exclude-server-4.4-driver-4.2 + * @group matrix-testing-exclude-server-5.0-driver-4.0 + * @group matrix-testing-exclude-server-5.0-driver-4.2 + */ + public function testIdIndexAndInfo(): void { - if (version_compare($this->getServerVersion(), '3.4.0', '<')) { - $this->markTestSkipped('idIndex and info are not supported'); - } - $server = $this->getPrimaryServer(); $insertOne = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1]); @@ -60,7 +61,7 @@ public function testIdIndexAndInfo() } } - public function testListCollectionsForNonexistentDatabase() + public function testListCollectionsForNonexistentDatabase(): void { $server = $this->getPrimaryServer(); @@ -73,14 +74,28 @@ public function testListCollectionsForNonexistentDatabase() $this->assertCount(0, $collections); } - public function testSessionOption() + public function testAuthorizedCollectionsOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } + (new CommandObserver())->observe( + function (): void { + $operation = new ListCollections( + $this->getDatabaseName(), + ['authorizedCollections' => true] + ); + $operation->execute($this->getPrimaryServer()); + }, + function (array $event): void { + $this->assertObjectHasAttribute('authorizedCollections', $event['started']->getCommand()); + $this->assertSame(true, $event['started']->getCommand()->authorizedCollections); + } + ); + } + + public function testSessionOption(): void + { (new CommandObserver())->observe( - function () { + function (): void { $operation = new ListCollections( $this->getDatabaseName(), ['session' => $this->createSession()] @@ -88,7 +103,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); diff --git a/tests/Operation/ListDatabaseNamesFunctionalTest.php b/tests/Operation/ListDatabaseNamesFunctionalTest.php index 2553c81fa..b0b728a89 100644 --- a/tests/Operation/ListDatabaseNamesFunctionalTest.php +++ b/tests/Operation/ListDatabaseNamesFunctionalTest.php @@ -5,11 +5,10 @@ use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListDatabaseNames; use MongoDB\Tests\CommandObserver; -use function version_compare; class ListDatabaseNamesFunctionalTest extends FunctionalTestCase { - public function testListDatabaseNames() + public function testListDatabaseNames(): void { $server = $this->getPrimaryServer(); @@ -19,12 +18,12 @@ public function testListDatabaseNames() $databases = null; (new CommandObserver())->observe( - function () use (&$databases, $server) { + function () use (&$databases, $server): void { $operation = new ListDatabaseNames(); $databases = $operation->execute($server); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('authorizedDatabases', $event['started']->getCommand()); $this->assertSame(true, $event['started']->getCommand()->nameOnly); } @@ -35,29 +34,25 @@ function (array $event) { } } - public function testAuthorizedDatabasesOption() + public function testAuthorizedDatabasesOption(): void { (new CommandObserver())->observe( - function () { + function (): void { $operation = new ListDatabaseNames( ['authorizedDatabases' => true] ); $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('authorizedDatabases', $event['started']->getCommand()); - $this->assertSame(true, $event['started']->getCommand()->nameOnly); + $this->assertSame(true, $event['started']->getCommand()->authorizedDatabases); } ); } - public function testFilterOption() + public function testFilterOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('listDatabase command "filter" option is not supported'); - } - $server = $this->getPrimaryServer(); $insertOne = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1]); @@ -73,21 +68,17 @@ public function testFilterOption() } } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new ListDatabaseNames( ['session' => $this->createSession()] ); $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); diff --git a/tests/Operation/ListDatabasesFunctionalTest.php b/tests/Operation/ListDatabasesFunctionalTest.php index 6e9bb1e58..45283ccfb 100644 --- a/tests/Operation/ListDatabasesFunctionalTest.php +++ b/tests/Operation/ListDatabasesFunctionalTest.php @@ -7,11 +7,10 @@ use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListDatabases; use MongoDB\Tests\CommandObserver; -use function version_compare; class ListDatabasesFunctionalTest extends FunctionalTestCase { - public function testListDatabases() + public function testListDatabases(): void { $server = $this->getPrimaryServer(); @@ -21,12 +20,12 @@ public function testListDatabases() $databases = null; (new CommandObserver())->observe( - function () use (&$databases, $server) { + function () use (&$databases, $server): void { $operation = new ListDatabases(); $databases = $operation->execute($server); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('authorizedDatabases', $event['started']->getCommand()); } ); @@ -38,28 +37,25 @@ function (array $event) { } } - public function testAuthorizedDatabasesOption() + public function testAuthorizedDatabasesOption(): void { (new CommandObserver())->observe( - function () { + function (): void { $operation = new ListDatabases( ['authorizedDatabases' => true] ); $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('authorizedDatabases', $event['started']->getCommand()); + $this->assertSame(true, $event['started']->getCommand()->authorizedDatabases); } ); } - public function testFilterOption() + public function testFilterOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('listDatabase command "filter" option is not supported'); - } - $server = $this->getPrimaryServer(); $insertOne = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1]); @@ -79,21 +75,17 @@ public function testFilterOption() } } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new ListDatabases( ['session' => $this->createSession()] ); $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); diff --git a/tests/Operation/ListIndexesFunctionalTest.php b/tests/Operation/ListIndexesFunctionalTest.php index 7c5fb8222..98aef09f2 100644 --- a/tests/Operation/ListIndexesFunctionalTest.php +++ b/tests/Operation/ListIndexesFunctionalTest.php @@ -4,18 +4,15 @@ use MongoDB\Model\IndexInfo; use MongoDB\Model\IndexInfoIterator; -use MongoDB\Operation\DropCollection; use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListIndexes; use MongoDB\Tests\CommandObserver; -use function version_compare; class ListIndexesFunctionalTest extends FunctionalTestCase { - public function testListIndexesForNewlyCreatedCollection() + public function testListIndexesForNewlyCreatedCollection(): void { - $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName()); - $operation->execute($this->getPrimaryServer()); + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); $insertOne = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1]); $writeResult = $insertOne->execute($this->getPrimaryServer()); @@ -35,10 +32,9 @@ public function testListIndexesForNewlyCreatedCollection() } } - public function testListIndexesForNonexistentCollection() + public function testListIndexesForNonexistentCollection(): void { - $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName()); - $operation->execute($this->getPrimaryServer()); + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); $operation = new ListIndexes($this->getDatabaseName(), $this->getCollectionName()); $indexes = $operation->execute($this->getPrimaryServer()); @@ -46,14 +42,10 @@ public function testListIndexesForNonexistentCollection() $this->assertCount(0, $indexes); } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new ListIndexes( $this->getDatabaseName(), $this->getCollectionName(), @@ -62,7 +54,7 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); diff --git a/tests/Operation/ListIndexesTest.php b/tests/Operation/ListIndexesTest.php index c8b845080..aa3b75e7a 100644 --- a/tests/Operation/ListIndexesTest.php +++ b/tests/Operation/ListIndexesTest.php @@ -7,10 +7,8 @@ class ListIndexesTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new ListIndexes($this->getDatabaseName(), $this->getCollectionName(), $options); diff --git a/tests/Operation/MapReduceFunctionalTest.php b/tests/Operation/MapReduceFunctionalTest.php index ddce2a963..55831dd2f 100644 --- a/tests/Operation/MapReduceFunctionalTest.php +++ b/tests/Operation/MapReduceFunctionalTest.php @@ -5,24 +5,30 @@ use MongoDB\BSON\Javascript; use MongoDB\Driver\BulkWrite; use MongoDB\MapReduceResult; -use MongoDB\Operation\DropCollection; use MongoDB\Operation\Find; use MongoDB\Operation\MapReduce; use MongoDB\Tests\CommandObserver; + use function is_object; use function iterator_to_array; use function usort; use function version_compare; +/** + * @group matrix-testing-exclude-server-4.4-driver-4.0 + * @group matrix-testing-exclude-server-4.4-driver-4.2 + * @group matrix-testing-exclude-server-5.0-driver-4.0 + * @group matrix-testing-exclude-server-5.0-driver-4.2 + */ class MapReduceFunctionalTest extends FunctionalTestCase { - public function testDefaultReadConcernIsOmitted() + public function testDefaultReadConcernIsOmitted(): void { // Collection must exist for mapReduce command - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); (new CommandObserver())->observe( - function () { + function (): void { $operation = new MapReduce( $this->getDatabaseName(), $this->getCollectionName(), @@ -34,19 +40,20 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); } ); } - public function testDefaultWriteConcernIsOmitted() + public function testDefaultWriteConcernIsOmitted(): void { // Collection must exist for mapReduce command - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName() . '.output'); (new CommandObserver())->observe( - function () { + function (): void { $operation = new MapReduce( $this->getDatabaseName(), $this->getCollectionName(), @@ -58,16 +65,13 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); } ); - - $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName() . '.output'); - $operation->execute($this->getPrimaryServer()); } - public function testFinalize() + public function testFinalize(): void { $this->createFixtures(3); @@ -82,7 +86,7 @@ public function testFinalize() $this->assertNotNull($result); } - public function testResult() + public function testResult(): void { $this->createFixtures(3); @@ -101,11 +105,9 @@ public function testResult() } } - public function testResultIncludesTimingWithVerboseOption() + public function testResultIncludesTimingWithVerboseOption(): void { - if (version_compare($this->getServerVersion(), '4.3.0', '>=')) { - $this->markTestSkipped('mapReduce statistics are no longer exposed'); - } + $this->skipIfServerVersion('>=', '4.3.0', 'mapReduce statistics are no longer exposed'); $this->createFixtures(3); @@ -122,11 +124,9 @@ public function testResultIncludesTimingWithVerboseOption() $this->assertNotEmpty($result->getTiming()); } - public function testResultDoesNotIncludeTimingWithoutVerboseOption() + public function testResultDoesNotIncludeTimingWithoutVerboseOption(): void { - if (version_compare($this->getServerVersion(), '4.3.0', '>=')) { - $this->markTestSkipped('mapReduce statistics are no longer exposed'); - } + $this->skipIfServerVersion('>=', '4.3.0', 'mapReduce statistics are no longer exposed'); $this->createFixtures(3); @@ -143,16 +143,12 @@ public function testResultDoesNotIncludeTimingWithoutVerboseOption() $this->assertEmpty($result->getTiming()); } - public function testSessionOption() + public function testSessionOption(): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); - } - $this->createFixtures(3); (new CommandObserver())->observe( - function () { + function (): void { $operation = new MapReduce( $this->getDatabaseName(), $this->getCollectionName(), @@ -164,22 +160,18 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); } - public function testBypassDocumentValidationSetWhenTrue() + public function testBypassDocumentValidationSetWhenTrue(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - $this->createFixtures(1); (new CommandObserver())->observe( - function () { + function (): void { $operation = new MapReduce( $this->getDatabaseName(), $this->getCollectionName(), @@ -191,23 +183,19 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); } ); } - public function testBypassDocumentValidationUnsetWhenFalse() + public function testBypassDocumentValidationUnsetWhenFalse(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - $this->createFixtures(1); (new CommandObserver())->observe( - function () { + function (): void { $operation = new MapReduce( $this->getDatabaseName(), $this->getCollectionName(), @@ -219,16 +207,14 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); } ); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ - public function testTypeMapOptionWithInlineResults(array $typeMap = null, array $expectedDocuments) + /** @dataProvider provideTypeMapOptionsAndExpectedDocuments */ + public function testTypeMapOptionWithInlineResults(?array $typeMap, array $expectedDocuments): void { $this->createFixtures(3); @@ -272,16 +258,15 @@ public function provideTypeMapOptionsAndExpectedDocuments() ]; } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ - public function testTypeMapOptionWithOutputCollection(array $typeMap = null, array $expectedDocuments) + /** @dataProvider provideTypeMapOptionsAndExpectedDocuments */ + public function testTypeMapOptionWithOutputCollection(?array $typeMap, array $expectedDocuments): void { $this->createFixtures(3); $map = new Javascript('function() { emit(this.x, this.y); }'); $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); $out = $this->getCollectionName() . '.output'; + $this->dropCollection($this->getDatabaseName(), $out); $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['typeMap' => $typeMap]); $results = iterator_to_array($operation->execute($this->getPrimaryServer())); @@ -292,18 +277,14 @@ public function testTypeMapOptionWithOutputCollection(array $typeMap = null, arr $cursor = $operation->execute($this->getPrimaryServer()); $this->assertEquals($this->sortResults($expectedDocuments), $this->sortResults(iterator_to_array($cursor))); - - $operation = new DropCollection($this->getDatabaseName(), $out); - $operation->execute($this->getPrimaryServer()); } /** * Create data fixtures. - * - * @param integer $n */ - private function createFixtures($n) + private function createFixtures(int $n): void { + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); $bulkWrite = new BulkWrite(['ordered' => true]); for ($i = 1; $i <= $n; $i++) { @@ -316,9 +297,9 @@ private function createFixtures($n) $this->assertEquals($n * 2, $result->getInsertedCount()); } - private function sortResults(array $results) : array + private function sortResults(array $results): array { - $sortFunction = static function ($resultA, $resultB) : int { + $sortFunction = static function ($resultA, $resultB): int { $idA = is_object($resultA) ? $resultA->_id : $resultA['_id']; $idB = is_object($resultB) ? $resultB->_id : $resultB['_id']; diff --git a/tests/Operation/MapReduceTest.php b/tests/Operation/MapReduceTest.php index 635dffb06..2f155aa84 100644 --- a/tests/Operation/MapReduceTest.php +++ b/tests/Operation/MapReduceTest.php @@ -2,18 +2,18 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; use MongoDB\BSON\Javascript; use MongoDB\BSON\ObjectId; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Model\BSONDocument; use MongoDB\Operation\MapReduce; use stdClass; class MapReduceTest extends TestCase { - /** - * @dataProvider provideInvalidOutValues - */ - public function testConstructorOutArgumentTypeCheck($out) + /** @dataProvider provideInvalidOutValues */ + public function testConstructorOutArgumentTypeCheck($out): void { $map = new Javascript('function() { emit(this.x, this.y); }'); $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); @@ -27,10 +27,33 @@ public function provideInvalidOutValues() return $this->wrapValuesForDataProvider([123, 3.14, true]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideDeprecatedOutValues */ + public function testConstructorOutArgumentDeprecations($out): void + { + $map = new Javascript('function() { emit(this.x, this.y); }'); + $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); + + $this->assertDeprecated(function () use ($map, $reduce, $out): void { + new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out); + }); + } + + public function provideDeprecatedOutValues(): array + { + return [ + 'nonAtomic:array' => [['nonAtomic' => false]], + 'nonAtomic:object' => [(object) ['nonAtomic' => false]], + 'nonAtomic:Serializable' => [new BSONDocument(['nonAtomic' => false])], + 'nonAtomic:Document' => [Document::fromPHP(['nonAtomic' => false])], + 'sharded:array' => [['sharded' => false]], + 'sharded:object' => [(object) ['sharded' => false]], + 'sharded:Serializable' => [new BSONDocument(['sharded' => false])], + 'sharded:Document' => [Document::fromPHP(['sharded' => false])], + ]; + } + + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $map = new Javascript('function() { emit(this.x, this.y); }'); $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); diff --git a/tests/Operation/ModifyCollectionFunctionalTest.php b/tests/Operation/ModifyCollectionFunctionalTest.php index 0fd22aa6c..5e971a0c8 100644 --- a/tests/Operation/ModifyCollectionFunctionalTest.php +++ b/tests/Operation/ModifyCollectionFunctionalTest.php @@ -4,13 +4,21 @@ use MongoDB\Operation\CreateIndexes; use MongoDB\Operation\ModifyCollection; -use function array_key_exists; class ModifyCollectionFunctionalTest extends FunctionalTestCase { - public function testCollMod() + /** + * @group matrix-testing-exclude-server-4.2-driver-4.0-topology-sharded_cluster + * @group matrix-testing-exclude-server-4.4-driver-4.0-topology-sharded_cluster + * @group matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster + */ + public function testCollMod(): void { - $this->createCollection(); + if ($this->isShardedCluster()) { + $this->markTestSkipped('Sharded clusters may report result inconsistently'); + } + + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $indexes = [['key' => ['lastAccess' => 1], 'expireAfterSeconds' => 3]]; $createIndexes = new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), $indexes); @@ -24,19 +32,7 @@ public function testCollMod() ); $result = $modifyCollection->execute($this->getPrimaryServer()); - if (array_key_exists('raw', $result)) { - /* Sharded environment, where we only assert if a shard had a successful update. For - * non-primary shards that don't have chunks for the collection, the result contains a - * "ns does not exist" error. */ - foreach ($result['raw'] as $shard) { - if (array_key_exists('ok', $shard) && $shard['ok'] == 1) { - $this->assertSame(3, $shard['expireAfterSeconds_old']); - $this->assertSame(1000, $shard['expireAfterSeconds_new']); - } - } - } else { - $this->assertSame(3, $result['expireAfterSeconds_old']); - $this->assertSame(1000, $result['expireAfterSeconds_new']); - } + $this->assertSame(3, $result['expireAfterSeconds_old']); + $this->assertSame(1000, $result['expireAfterSeconds_new']); } } diff --git a/tests/Operation/ModifyCollectionTest.php b/tests/Operation/ModifyCollectionTest.php index 48a48d39d..4fcda8b72 100644 --- a/tests/Operation/ModifyCollectionTest.php +++ b/tests/Operation/ModifyCollectionTest.php @@ -7,17 +7,15 @@ class ModifyCollectionTest extends TestCase { - public function testConstructorEmptyCollectionOptions() + public function testConstructorEmptyCollectionOptions(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$collectionOptions is empty'); new ModifyCollection($this->getDatabaseName(), $this->getCollectionName(), []); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new ModifyCollection($this->getDatabaseName(), $this->getCollectionName(), [], $options); diff --git a/tests/Operation/RenameCollectionFunctionalTest.php b/tests/Operation/RenameCollectionFunctionalTest.php new file mode 100644 index 000000000..21a88e25d --- /dev/null +++ b/tests/Operation/RenameCollectionFunctionalTest.php @@ -0,0 +1,148 @@ +toCollectionName = $this->getCollectionName() . '.renamed'; + $this->dropCollection($this->getDatabaseName(), $this->toCollectionName); + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + } + + public function testDefaultWriteConcernIsOmitted(): void + { + (new CommandObserver())->observe( + function (): void { + $server = $this->getPrimaryServer(); + + $insertOne = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1]); + $writeResult = $insertOne->execute($server); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $operation = new RenameCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + $this->getDatabaseName(), + $this->toCollectionName, + ['writeConcern' => $this->createDefaultWriteConcern()] + ); + + $operation->execute($server); + }, + function (array $event): void { + $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); + } + ); + } + + public function testRenameCollectionToNonexistentTarget(): void + { + $server = $this->getPrimaryServer(); + + $insertOne = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['_id' => 1]); + $writeResult = $insertOne->execute($server); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $operation = new RenameCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + $this->getDatabaseName(), + $this->toCollectionName + ); + $commandResult = $operation->execute($server); + + $this->assertCommandSucceeded($commandResult); + $this->assertCollectionDoesNotExist($this->getCollectionName()); + $this->assertCollectionExists($this->toCollectionName); + + $operation = new FindOne($this->getDatabaseName(), $this->toCollectionName, []); + $this->assertSameDocument(['_id' => 1], $operation->execute($server)); + } + + public function testRenameCollectionExistingTarget(): void + { + $server = $this->getPrimaryServer(); + + $insertOne = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['_id' => 1]); + $writeResult = $insertOne->execute($server); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $insertOne = new InsertOne($this->getDatabaseName(), $this->toCollectionName, ['_id' => 1]); + $writeResult = $insertOne->execute($server); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $this->expectException(CommandException::class); + + // TODO: mongos returns an inconsistent error code (see: SERVER-60632) + if (! $this->isShardedCluster()) { + $this->expectExceptionCode(self::$errorCodeNamespaceExists); + } + + $operation = new RenameCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + $this->getDatabaseName(), + $this->toCollectionName + ); + $operation->execute($server); + } + + public function testRenameNonexistentCollection(): void + { + $this->expectException(CommandException::class); + $this->expectExceptionCode(self::$errorCodeNamespaceNotFound); + + $operation = new RenameCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + $this->getDatabaseName(), + $this->toCollectionName + ); + $operation->execute($this->getPrimaryServer()); + } + + public function testSessionOption(): void + { + (new CommandObserver())->observe( + function (): void { + $server = $this->getPrimaryServer(); + + $insertOne = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1]); + $writeResult = $insertOne->execute($server); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $operation = new RenameCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + $this->getDatabaseName(), + $this->toCollectionName, + ['session' => $this->createSession()] + ); + + $operation->execute($server); + }, + function (array $event): void { + $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); + } + ); + } +} diff --git a/tests/Operation/RenameCollectionTest.php b/tests/Operation/RenameCollectionTest.php new file mode 100644 index 000000000..e13402147 --- /dev/null +++ b/tests/Operation/RenameCollectionTest.php @@ -0,0 +1,45 @@ +expectException(InvalidArgumentException::class); + new RenameCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + $this->getDatabaseName(), + $this->getCollectionName() . '.renamed', + $options + ); + } + + public function provideInvalidConstructorOptions() + { + $options = []; + + foreach ($this->getInvalidSessionValues() as $value) { + $options[][] = ['session' => $value]; + } + + foreach ($this->getInvalidArrayValues() as $value) { + $options[][] = ['typeMap' => $value]; + } + + foreach ($this->getInvalidWriteConcernValues() as $value) { + $options[][] = ['writeConcern' => $value]; + } + + foreach ($this->getInvalidBooleanValues() as $value) { + $options[][] = ['dropTarget' => $value]; + } + + return $options; + } +} diff --git a/tests/Operation/ReplaceOneTest.php b/tests/Operation/ReplaceOneTest.php index a639e2252..fd057921e 100644 --- a/tests/Operation/ReplaceOneTest.php +++ b/tests/Operation/ReplaceOneTest.php @@ -3,24 +3,19 @@ namespace MongoDB\Tests\Operation; use MongoDB\Exception\InvalidArgumentException; -use MongoDB\Model\BSONDocument; use MongoDB\Operation\ReplaceOne; class ReplaceOneTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), $filter, ['y' => 1]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorReplacementArgumentTypeCheck($replacement) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorReplacementArgumentTypeCheck($replacement): void { $this->expectException(InvalidArgumentException::class); new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); @@ -30,36 +25,27 @@ public function testConstructorReplacementArgumentTypeCheck($replacement) * @dataProvider provideReplacementDocuments * @doesNotPerformAssertions */ - public function testConstructorReplacementArgument($replacement) + public function testConstructorReplacementArgument($replacement): void { new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); } - /** - * @dataProvider provideUpdateDocuments - */ - public function testConstructorReplacementArgumentRequiresNoOperators($replacement) + /** @dataProvider provideUpdateDocuments */ + public function testConstructorReplacementArgumentProhibitsUpdateDocument($replacement): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('First key in $replacement argument is an update operator'); + $this->expectExceptionMessage('First key in $replacement is an update operator'); new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); } - public function provideReplacementDocuments() - { - return $this->wrapValuesForDataProvider([ - ['y' => 1], - (object) ['y' => 1], - new BSONDocument(['y' => 1]), - ]); - } - - public function provideUpdateDocuments() + /** + * @dataProvider provideUpdatePipelines + * @dataProvider provideEmptyUpdatePipelinesExcludingArray + */ + public function testConstructorReplacementArgumentProhibitsUpdatePipeline($replacement): void { - return $this->wrapValuesForDataProvider([ - ['$set' => ['y' => 1]], - (object) ['$set' => ['y' => 1]], - new BSONDocument(['$set' => ['y' => 1]]), - ]); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$replacement is an update pipeline'); + new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); } } diff --git a/tests/Operation/TestCase.php b/tests/Operation/TestCase.php index 2de8b0e68..684899ec5 100644 --- a/tests/Operation/TestCase.php +++ b/tests/Operation/TestCase.php @@ -2,6 +2,10 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; +use MongoDB\BSON\PackedArray; +use MongoDB\Model\BSONArray; +use MongoDB\Model\BSONDocument; use MongoDB\Tests\TestCase as BaseTestCase; /** @@ -9,4 +13,65 @@ */ abstract class TestCase extends BaseTestCase { + public function provideReplacementDocuments(): array + { + return [ + 'replacement:array' => [['x' => 1]], + 'replacement:object' => [(object) ['x' => 1]], + 'replacement:Serializable' => [new BSONDocument(['x' => 1])], + 'replacement:Document' => [Document::fromPHP(['x' => 1])], + /* Note: empty arrays could also express a pipeline, but PHPLIB + * interprets them as a replacement document for BC. */ + 'empty_replacement:array' => [[]], + 'empty_replacement:object' => [(object) []], + 'empty_replacement:Serializable' => [new BSONDocument([])], + 'empty_replacement:Document' => [Document::fromPHP([])], + ]; + } + + public function provideUpdateDocuments(): array + { + return [ + 'update:array' => [['$set' => ['x' => 1]]], + 'update:object' => [(object) ['$set' => ['x' => 1]]], + 'update:Serializable' => [new BSONDocument(['$set' => ['x' => 1]])], + 'update:Document' => [Document::fromPHP(['$set' => ['x' => 1]])], + ]; + } + + public function provideUpdatePipelines(): array + { + return [ + 'pipeline:array' => [[['$set' => ['x' => 1]]]], + 'pipeline:Serializable' => [new BSONArray([['$set' => ['x' => 1]]])], + 'pipeline:PackedArray' => [PackedArray::fromPHP([['$set' => ['x' => 1]]])], + ]; + } + + public function provideEmptyUpdatePipelines(): array + { + /* Empty update pipelines are accepted by the update and findAndModify + * commands (as NOPs); however, they are not supported for updates in + * libmongoc because empty arrays and documents have the same bson_t + * representation (libmongoc considers it an empty replacement for BC). + * For consistency, PHPLIB rejects empty pipelines for updateOne, + * updateMany, and findOneAndUpdate operations. Replace operations + * interpret empty arrays as replacement documents for BC, but rejects + * other representations. */ + return [ + 'empty_pipeline:array' => [[]], + 'empty_pipeline:Serializable' => [new BSONArray([])], + 'empty_pipeline:PackedArray' => [PackedArray::fromPHP([])], + ]; + } + + public function provideEmptyUpdatePipelinesExcludingArray(): array + { + /* This data provider is used for replace operations, which accept empty + * arrays as replacement documents for BC. */ + return [ + 'empty_pipeline:Serializable' => [new BSONArray([])], + 'empty_pipeline:PackedArray' => [PackedArray::fromPHP([])], + ]; + } } diff --git a/tests/Operation/UpdateFunctionalTest.php b/tests/Operation/UpdateFunctionalTest.php index fc0c1552d..d0a92679f 100644 --- a/tests/Operation/UpdateFunctionalTest.php +++ b/tests/Operation/UpdateFunctionalTest.php @@ -7,34 +7,91 @@ use MongoDB\Driver\BulkWrite; use MongoDB\Driver\WriteConcern; use MongoDB\Exception\BadMethodCallException; +use MongoDB\Exception\UnsupportedException; use MongoDB\Operation\Update; use MongoDB\Tests\CommandObserver; use MongoDB\UpdateResult; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; -use function version_compare; +use stdClass; + +use function is_array; class UpdateFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; - /** @var Collection */ private $collection; - private function doSetUp() + public function setUp(): void { parent::setUp(); $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); } - public function testSessionOption() + /** @dataProvider provideFilterDocuments */ + public function testFilterDocuments($filter, stdClass $expectedFilter): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new Update( + $this->getDatabaseName(), + $this->getCollectionName(), + $filter, + ['$set' => ['x' => 1]] + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedFilter): void { + $this->assertEquals($expectedFilter, $event['started']->getCommand()->updates[0]->q ?? null); + } + ); + } + + /** + * @dataProvider provideReplacementDocuments + * @dataProvider provideUpdateDocuments + * @dataProvider provideUpdatePipelines + * @dataProvider provideReplacementDocumentLikePipeline + */ + public function testUpdateDocuments($update, $expectedUpdate): void { - if (version_compare($this->getServerVersion(), '3.6.0', '<')) { - $this->markTestSkipped('Sessions are not supported'); + if (is_array($expectedUpdate)) { + $this->skipIfServerVersion('<', '4.2.0', 'Pipeline-style updates are not supported'); } (new CommandObserver())->observe( - function () { + function () use ($update): void { + $operation = new Update( + $this->getDatabaseName(), + $this->getCollectionName(), + ['x' => 1], + $update + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedUpdate): void { + $this->assertEquals($expectedUpdate, $event['started']->getCommand()->updates[0]->u ?? null); + } + ); + } + + public function provideReplacementDocumentLikePipeline(): array + { + /* Note: libmongoc encodes this replacement document as a BSON array + * because it resembles an update pipeline (see: CDRIVER-4658). */ + return [ + 'replacement_like_pipeline' => [ + (object) ['0' => ['$set' => ['x' => 1]]], + [(object) ['$set' => (object) ['x' => 1]]], + ], + ]; + } + + public function testSessionOption(): void + { + (new CommandObserver())->observe( + function (): void { $operation = new Update( $this->getDatabaseName(), $this->getCollectionName(), @@ -45,20 +102,16 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); } ); } - public function testBypassDocumentValidationSetWhenTrue() + public function testBypassDocumentValidationSetWhenTrue(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new Update( $this->getDatabaseName(), $this->getCollectionName(), @@ -69,21 +122,17 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); } ); } - public function testBypassDocumentValidationUnsetWhenFalse() + public function testBypassDocumentValidationUnsetWhenFalse(): void { - if (version_compare($this->getServerVersion(), '3.2.0', '<')) { - $this->markTestSkipped('bypassDocumentValidation is not supported'); - } - (new CommandObserver())->observe( - function () { + function (): void { $operation = new Update( $this->getDatabaseName(), $this->getCollectionName(), @@ -94,13 +143,31 @@ function () { $operation->execute($this->getPrimaryServer()); }, - function (array $event) { + function (array $event): void { $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); } ); } - public function testUpdateOne() + public function testHintOptionAndUnacknowledgedWriteConcernUnsupportedClientSideError(): void + { + $this->skipIfServerVersion('>=', '4.2.0', 'hint is supported'); + + $operation = new Update( + $this->getDatabaseName(), + $this->getCollectionName(), + ['_id' => 1], + ['$inc' => ['x' => 1]], + ['hint' => '_id_', 'writeConcern' => new WriteConcern(0)] + ); + + $this->expectException(UnsupportedException::class); + $this->expectExceptionMessage('Hint is not supported by the server executing this operation'); + + $operation->execute($this->getPrimaryServer()); + } + + public function testUpdateOne(): void { $this->createFixtures(3); @@ -125,7 +192,7 @@ public function testUpdateOne() $this->assertSameDocuments($expected, $this->collection->find()); } - public function testUpdateMany() + public function testUpdateMany(): void { $this->createFixtures(3); @@ -151,7 +218,7 @@ public function testUpdateMany() $this->assertSameDocuments($expected, $this->collection->find()); } - public function testUpdateManyWithExistingId() + public function testUpdateManyWithExistingId(): void { $this->createFixtures(3); @@ -178,7 +245,7 @@ public function testUpdateManyWithExistingId() $this->assertSameDocuments($expected, $this->collection->find()); } - public function testUpdateManyWithGeneratedId() + public function testUpdateManyWithGeneratedId(): void { $this->createFixtures(3); @@ -218,40 +285,32 @@ public function testUnacknowledgedWriteConcern() return $result; } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesMatchedCount(UpdateResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesMatchedCount(UpdateResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); $result->getMatchedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesModifiedCount(UpdateResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesModifiedCount(UpdateResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); $result->getModifiedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesUpsertedCount(UpdateResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesUpsertedCount(UpdateResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); $result->getUpsertedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ - public function testUnacknowledgedWriteConcernAccessesUpsertedId(UpdateResult $result) + /** @depends testUnacknowledgedWriteConcern */ + public function testUnacknowledgedWriteConcernAccessesUpsertedId(UpdateResult $result): void { $this->expectException(BadMethodCallException::class); $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); @@ -260,10 +319,8 @@ public function testUnacknowledgedWriteConcernAccessesUpsertedId(UpdateResult $r /** * Create data fixtures. - * - * @param integer $n */ - private function createFixtures($n) + private function createFixtures(int $n): void { $bulkWrite = new BulkWrite(['ordered' => true]); diff --git a/tests/Operation/UpdateManyTest.php b/tests/Operation/UpdateManyTest.php index 70537c0bc..7f12207c8 100644 --- a/tests/Operation/UpdateManyTest.php +++ b/tests/Operation/UpdateManyTest.php @@ -3,24 +3,19 @@ namespace MongoDB\Tests\Operation; use MongoDB\Exception\InvalidArgumentException; -use MongoDB\Model\BSONDocument; use MongoDB\Operation\UpdateMany; class UpdateManyTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); new UpdateMany($this->getDatabaseName(), $this->getCollectionName(), $filter, ['$set' => ['x' => 1]]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorUpdateArgumentTypeCheck($update) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorUpdateArgumentTypeCheck($update): void { $this->expectException(InvalidArgumentException::class); new UpdateMany($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); @@ -28,38 +23,22 @@ public function testConstructorUpdateArgumentTypeCheck($update) /** * @dataProvider provideUpdateDocuments + * @dataProvider provideUpdatePipelines * @doesNotPerformAssertions */ - public function testConstructorUpdateArgument($update) + public function testConstructorUpdateArgument($update): void { new UpdateMany($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } /** * @dataProvider provideReplacementDocuments + * @dataProvider provideEmptyUpdatePipelines */ - public function testConstructorUpdateArgumentRequiresOperators($replacement) + public function testConstructorUpdateArgumentProhibitsReplacementDocumentOrEmptyPipeline($update): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Expected an update document with operator as first key or a pipeline'); - new UpdateMany($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); - } - - public function provideReplacementDocuments() - { - return $this->wrapValuesForDataProvider([ - ['y' => 1], - (object) ['y' => 1], - new BSONDocument(['y' => 1]), - ]); - } - - public function provideUpdateDocuments() - { - return $this->wrapValuesForDataProvider([ - ['$set' => ['y' => 1]], - (object) ['$set' => ['y' => 1]], - new BSONDocument(['$set' => ['y' => 1]]), - ]); + $this->expectExceptionMessage('Expected update operator(s) or non-empty pipeline for $update'); + new UpdateMany($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } } diff --git a/tests/Operation/UpdateOneTest.php b/tests/Operation/UpdateOneTest.php index 6aec7a5ba..657f3d650 100644 --- a/tests/Operation/UpdateOneTest.php +++ b/tests/Operation/UpdateOneTest.php @@ -3,24 +3,19 @@ namespace MongoDB\Tests\Operation; use MongoDB\Exception\InvalidArgumentException; -use MongoDB\Model\BSONDocument; use MongoDB\Operation\UpdateOne; class UpdateOneTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); new UpdateOne($this->getDatabaseName(), $this->getCollectionName(), $filter, ['$set' => ['x' => 1]]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorUpdateArgumentTypeCheck($update) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorUpdateArgumentTypeCheck($update): void { $this->expectException(InvalidArgumentException::class); new UpdateOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); @@ -28,38 +23,22 @@ public function testConstructorUpdateArgumentTypeCheck($update) /** * @dataProvider provideUpdateDocuments + * @dataProvider provideUpdatePipelines * @doesNotPerformAssertions */ - public function testConstructorUpdateArgument($update) + public function testConstructorUpdateArgument($update): void { new UpdateOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } /** * @dataProvider provideReplacementDocuments + * @dataProvider provideEmptyUpdatePipelines */ - public function testConstructorUpdateArgumentRequiresOperators($replacement) + public function testConstructorUpdateArgumentProhibitsReplacementDocumentOrEmptyPipeline($update): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Expected an update document with operator as first key or a pipeline'); - new UpdateOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); - } - - public function provideReplacementDocuments() - { - return $this->wrapValuesForDataProvider([ - ['y' => 1], - (object) ['y' => 1], - new BSONDocument(['y' => 1]), - ]); - } - - public function provideUpdateDocuments() - { - return $this->wrapValuesForDataProvider([ - ['$set' => ['y' => 1]], - (object) ['$set' => ['y' => 1]], - new BSONDocument(['$set' => ['y' => 1]]), - ]); + $this->expectExceptionMessage('Expected update operator(s) or non-empty pipeline for $update'); + new UpdateOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } } diff --git a/tests/Operation/UpdateTest.php b/tests/Operation/UpdateTest.php index ddb61a175..de9fcbadd 100644 --- a/tests/Operation/UpdateTest.php +++ b/tests/Operation/UpdateTest.php @@ -2,35 +2,30 @@ namespace MongoDB\Tests\Operation; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Update; class UpdateTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorFilterArgumentTypeCheck($filter) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$filter to have type "array or object" but found "[\w ]+"/'); new Update($this->getDatabaseName(), $this->getCollectionName(), $filter, ['$set' => ['x' => 1]]); } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testConstructorUpdateArgumentTypeCheck($update) + /** @dataProvider provideInvalidDocumentValues */ + public function testConstructorUpdateArgumentTypeCheck($update): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageMatches('/Expected \$update to have type "array or object" but found "[\w ]+"/'); new Update($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Update($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], ['y' => 1], $options); @@ -70,4 +65,48 @@ public function provideInvalidConstructorOptions() return $options; } + + /** + * @dataProvider provideReplacementDocuments + * @dataProvider provideEmptyUpdatePipelines + */ + public function testConstructorMultiOptionProhibitsReplacementDocumentOrEmptyPipeline($update): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('"multi" option cannot be true unless $update has update operator(s) or non-empty pipeline'); + new Update($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update, ['multi' => true]); + } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'arrayFilters' => [['x' => 1]], + 'bypassDocumentValidation' => true, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'multi' => true, + 'upsert' => true, + 'let' => ['a' => 3], + 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), + ]; + $operation = new Update($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], ['$set' => ['x' => 2]], $options); + + $expected = [ + 'update' => $this->getCollectionName(), + 'bypassDocumentValidation' => true, + 'updates' => [ + [ + 'q' => ['x' => 1], + 'u' => ['$set' => ['x' => 2]], + 'multi' => true, + 'upsert' => true, + 'arrayFilters' => [['x' => 1]], + 'hint' => '_id_', + 'collation' => (object) ['locale' => 'fr'], + ], + ], + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); + } } diff --git a/tests/Operation/WatchFunctionalTest.php b/tests/Operation/WatchFunctionalTest.php index 01d2d57f7..bc633c191 100644 --- a/tests/Operation/WatchFunctionalTest.php +++ b/tests/Operation/WatchFunctionalTest.php @@ -11,7 +11,6 @@ use MongoDB\Driver\Exception\ConnectionTimeoutException; use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\Exception\ServerException; -use MongoDB\Driver\Manager; use MongoDB\Driver\Monitoring\CommandSucceededEvent; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; @@ -22,21 +21,24 @@ use PHPUnit\Framework\ExpectationFailedException; use ReflectionClass; use stdClass; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; + use function array_diff_key; use function array_map; +use function assert; use function bin2hex; use function microtime; use function MongoDB\server_supports_feature; use function sprintf; -use function version_compare; +/** + * @group matrix-testing-exclude-server-4.2-driver-4.0-topology-sharded_cluster + * @group matrix-testing-exclude-server-4.4-driver-4.0-topology-sharded_cluster + * @group matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster + */ class WatchFunctionalTest extends FunctionalTestCase { - use SetUpTearDownTrait; - - const INTERRUPTED = 11601; - const NOT_MASTER = 10107; + public const INTERRUPTED = 11601; + public const NOT_PRIMARY = 10107; /** @var integer */ private static $wireVersionForStartAtOperationTime = 7; @@ -44,23 +46,21 @@ class WatchFunctionalTest extends FunctionalTestCase /** @var array */ private $defaultOptions = ['maxAwaitTimeMS' => 500]; - private function doSetUp() + public function setUp(): void { parent::setUp(); $this->skipIfChangeStreamIsNotSupported(); - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); } /** * Prose test 1: "ChangeStream must continuously track the last seen * resumeToken" */ - public function testGetResumeToken() + public function testGetResumeToken(): void { - if ($this->isPostBatchResumeTokenSupported()) { - $this->markTestSkipped('postBatchResumeToken is supported'); - } + $this->skipIfServerVersion('>=', '4.0.7', 'postBatchResumeToken is supported'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -103,21 +103,19 @@ public function testGetResumeToken() * Expected result: getResumeToken must return the _id of the previous * document returned. */ - public function testGetResumeTokenWithPostBatchResumeToken() + public function testGetResumeTokenWithPostBatchResumeToken(): void { - if (! $this->isPostBatchResumeTokenSupported()) { - $this->markTestSkipped('postBatchResumeToken is not supported'); - } + $this->skipIfServerVersion('<', '4.0.7', 'postBatchResumeToken is not supported'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $events = []; (new CommandObserver())->observe( - function () use ($operation, &$changeStream) { + function () use ($operation, &$changeStream): void { $changeStream = $operation->execute($this->getPrimaryServer()); }, - function (array $event) use (&$events) { + function (array $event) use (&$events): void { $events[] = $event; } ); @@ -136,10 +134,10 @@ function (array $event) use (&$events) { $lastEvent = null; (new CommandObserver())->observe( - function () use ($changeStream) { + function () use ($changeStream): void { $this->advanceCursorUntilValid($changeStream); }, - function (array $event) use (&$lastEvent) { + function (array $event) use (&$lastEvent): void { $lastEvent = $event; } ); @@ -154,15 +152,15 @@ function (array $event) use (&$lastEvent) { $this->assertSameDocument($postBatchResumeToken, $changeStream->getResumeToken()); } - public function testNextResumesAfterConnectionException() + public function testNextResumesAfterConnectionException(): void { $this->skipIfIsShardedCluster('initial aggregate command times out due to socketTimeoutMS'); /* In order to trigger a dropped connection, we'll use a new client with * a socket timeout that is less than the change stream's maxAwaitTimeMS * option. */ - $manager = new Manager(static::getUri(), ['socketTimeoutMS' => 50]); - $primaryServer = $manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY)); + $manager = static::createTestManager(null, ['socketTimeoutMS' => 50]); + $primaryServer = $manager->selectServer(); $operation = new Watch($manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($primaryServer); @@ -171,10 +169,10 @@ public function testNextResumesAfterConnectionException() $commands = []; (new CommandObserver())->observe( - function () use ($changeStream) { + function () use ($changeStream): void { $changeStream->next(); }, - function (array $event) use (&$commands) { + function (array $event) use (&$commands): void { $commands[] = $event['started']->getCommandName(); } ); @@ -201,21 +199,19 @@ function (array $event) use (&$commands) { $this->assertSame($expectedCommands, $commands); } - public function testResumeBeforeReceivingAnyResultsIncludesPostBatchResumeToken() + public function testResumeBeforeReceivingAnyResultsIncludesPostBatchResumeToken(): void { - if (! $this->isPostBatchResumeTokenSupported()) { - $this->markTestSkipped('postBatchResumeToken is not supported'); - } + $this->skipIfServerVersion('<', '4.0.7', 'postBatchResumeToken is not supported'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $events = []; (new CommandObserver())->observe( - function () use ($operation, &$changeStream) { + function () use ($operation, &$changeStream): void { $changeStream = $operation->execute($this->getPrimaryServer()); }, - function (array $event) use (&$events) { + function (array $event) use (&$events): void { $events[] = $event; } ); @@ -227,17 +223,17 @@ function (array $event) use (&$events) { $this->assertFalse($changeStream->valid()); $this->forceChangeStreamResume(); - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $events = []; (new CommandObserver())->observe( - function () use ($changeStream) { + function () use ($changeStream): void { $changeStream->next(); }, - function (array $event) use (&$events) { + function (array $event) use (&$events): void { $events[] = $event; } ); @@ -258,7 +254,7 @@ function (array $event) use (&$events) { $this->assertFalse($changeStream->valid()); } - private function assertResumeAfter($expectedResumeToken, stdClass $command) + private function assertResumeAfter($expectedResumeToken, stdClass $command): void { $this->assertObjectHasAttribute('pipeline', $command); $this->assertIsArray($command->pipeline); @@ -273,25 +269,23 @@ private function assertResumeAfter($expectedResumeToken, stdClass $command) * >=4.0 and <4.0.7 that has not received any results yet MUST include a * startAtOperationTime option when resuming a changestream." */ - public function testResumeBeforeReceivingAnyResultsIncludesStartAtOperationTime() + public function testResumeBeforeReceivingAnyResultsIncludesStartAtOperationTime(): void { if (! $this->isStartAtOperationTimeSupported()) { $this->markTestSkipped('startAtOperationTime is not supported'); } - if ($this->isPostBatchResumeTokenSupported()) { - $this->markTestSkipped('postBatchResumeToken takes precedence over startAtOperationTime'); - } + $this->skipIfServerVersion('>=', '4.0.7', 'postBatchResumeToken takes precedence over startAtOperationTime'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $events = []; (new CommandObserver())->observe( - function () use ($operation, &$changeStream) { + function () use ($operation, &$changeStream): void { $changeStream = $operation->execute($this->getPrimaryServer()); }, - function (array $event) use (&$events) { + function (array $event) use (&$events): void { $events[] = $event; } ); @@ -306,17 +300,17 @@ function (array $event) use (&$events) { $this->assertFalse($changeStream->valid()); $this->forceChangeStreamResume(); - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $events = []; (new CommandObserver())->observe( - function () use ($changeStream) { + function () use ($changeStream): void { $changeStream->next(); }, - function (array $event) use (&$events) { + function (array $event) use (&$events): void { $events[] = $event; } ); @@ -337,7 +331,7 @@ function (array $event) use (&$events) { $this->assertFalse($changeStream->valid()); } - private function assertStartAtOperationTime(TimestampInterface $expectedOperationTime, stdClass $command) + private function assertStartAtOperationTime(TimestampInterface $expectedOperationTime, stdClass $command): void { $this->assertObjectHasAttribute('pipeline', $command); $this->assertIsArray($command->pipeline); @@ -347,7 +341,7 @@ private function assertStartAtOperationTime(TimestampInterface $expectedOperatio $this->assertEquals($expectedOperationTime, $command->pipeline[0]->{'$changeStream'}->startAtOperationTime); } - public function testRewindMultipleTimesWithResults() + public function testRewindMultipleTimesWithResults(): void { $this->skipIfIsShardedCluster('Cursor needs to be advanced multiple times and can\'t be rewound afterwards.'); @@ -357,7 +351,7 @@ public function testRewindMultipleTimesWithResults() $this->insertDocument(['x' => 1]); $this->insertDocument(['x' => 2]); - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $this->assertFalse($changeStream->valid()); @@ -365,7 +359,7 @@ public function testRewindMultipleTimesWithResults() $this->assertNull($changeStream->current()); // Subsequent rewind does not change iterator state - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $this->assertFalse($changeStream->valid()); @@ -379,7 +373,7 @@ public function testRewindMultipleTimesWithResults() /* Rewinding when the iterator is still at its first element is a NOP. * Note: PHPLIB-448 may see rewind() throw after any call to next() */ - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $this->assertTrue($changeStream->valid()); @@ -396,12 +390,12 @@ public function testRewindMultipleTimesWithResults() $changeStream->rewind(); } - public function testRewindMultipleTimesWithNoResults() + public function testRewindMultipleTimesWithNoResults(): void { $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $this->assertFalse($changeStream->valid()); @@ -409,7 +403,7 @@ public function testRewindMultipleTimesWithNoResults() $this->assertNull($changeStream->current()); // Subsequent rewind does not change iterator state - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $this->assertFalse($changeStream->valid()); @@ -423,7 +417,7 @@ public function testRewindMultipleTimesWithNoResults() /* Rewinding when the iterator hasn't advanced to an element is a NOP. * Note: PHPLIB-448 may see rewind() throw after any call to next() */ - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $this->assertFalse($changeStream->valid()); @@ -431,12 +425,12 @@ public function testRewindMultipleTimesWithNoResults() $this->assertNull($changeStream->current()); } - public function testNoChangeAfterResumeBeforeInsert() + public function testNoChangeAfterResumeBeforeInsert(): void { $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $this->assertFalse($changeStream->valid()); @@ -475,7 +469,7 @@ public function testNoChangeAfterResumeBeforeInsert() $this->assertMatchesDocument($expectedResult, $changeStream->current()); } - public function testResumeMultipleTimesInSuccession() + public function testResumeMultipleTimesInSuccession(): void { $this->skipIfIsShardedCluster('getMore may return empty response before periodicNoopIntervalSecs on sharded clusters.'); @@ -487,7 +481,7 @@ public function testResumeMultipleTimesInSuccession() * key. */ $this->forceChangeStreamResume(); - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $this->assertFalse($changeStream->valid()); @@ -555,7 +549,7 @@ public function testResumeMultipleTimesInSuccession() $this->insertDocument(['_id' => 3]); $this->forceChangeStreamResume(); - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $this->assertTrue($changeStream->valid()); @@ -596,7 +590,7 @@ public function testResumeMultipleTimesInSuccession() $this->assertMatchesDocument($expectedResult, $changeStream->current()); } - public function testKey() + public function testKey(): void { $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -604,7 +598,7 @@ public function testKey() $this->assertFalse($changeStream->valid()); $this->assertNull($changeStream->key()); - $this->assertNoCommandExecuted(function () use ($changeStream) { + $this->assertNoCommandExecuted(function () use ($changeStream): void { $changeStream->rewind(); }); $this->assertFalse($changeStream->valid()); @@ -635,7 +629,7 @@ public function testKey() $this->assertSame(1, $changeStream->key()); } - public function testNonEmptyPipeline() + public function testNonEmptyPipeline(): void { $pipeline = [['$project' => ['foo' => [0]]]]; @@ -662,7 +656,7 @@ public function testNonEmptyPipeline() * with a cursor id and an initial empty batch is not closed on the driver * side." */ - public function testInitialCursorIsNotClosed() + public function testInitialCursorIsNotClosed(): void { $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), []); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -670,9 +664,9 @@ public function testInitialCursorIsNotClosed() /* The spec requests that we assert that the cursor returned from the * aggregate command is not closed on the driver side. We will verify * this by checking that the cursor ID is non-zero and that libmongoc - * reports the cursor as alive. While the cursor ID is easily accessed - * through ChangeStream, we'll need to use reflection to access the - * internal Cursor and call isDead(). */ + * reports the cursor as alive. While the cursor ID is accessed through + * ChangeStream, we'll need to use reflection to access the internal + * Cursor and call isDead(). */ $this->assertNotEquals('0', (string) $changeStream->getCursorId()); $rc = new ReflectionClass(ChangeStream::class); @@ -694,13 +688,11 @@ public function testInitialCursorIsNotClosed() * response is missing the resume token (if wire version is < 8, this is a * driver-side error; for 8+, this is a server-side error)" */ - public function testResumeTokenNotFoundClientSideError() + public function testResumeTokenNotFoundClientSideError(): void { - if (version_compare($this->getServerVersion(), '4.1.8', '>=')) { - $this->markTestSkipped('Server rejects change streams that modify resume token (SERVER-37786)'); - } + $this->skipIfServerVersion('>=', '4.1.8', 'Server rejects change streams that modify resume token (SERVER-37786)'); - $pipeline = [['$project' => ['_id' => 0 ]]]; + $pipeline = [['$project' => ['_id' => 0]]]; $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $pipeline, $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -722,13 +714,11 @@ public function testResumeTokenNotFoundClientSideError() * response is missing the resume token (if wire version is < 8, this is a * driver-side error; for 8+, this is a server-side error)" */ - public function testResumeTokenNotFoundServerSideError() + public function testResumeTokenNotFoundServerSideError(): void { - if (version_compare($this->getServerVersion(), '4.1.8', '<')) { - $this->markTestSkipped('Server does not reject change streams that modify resume token'); - } + $this->skipIfServerVersion('<', '4.1.8', 'Server does not reject change streams that modify resume token'); - $pipeline = [['$project' => ['_id' => 0 ]]]; + $pipeline = [['$project' => ['_id' => 0]]]; $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $pipeline, $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -745,11 +735,9 @@ public function testResumeTokenNotFoundServerSideError() * response is missing the resume token (if wire version is < 8, this is a * driver-side error; for 8+, this is a server-side error)" */ - public function testResumeTokenInvalidTypeClientSideError() + public function testResumeTokenInvalidTypeClientSideError(): void { - if (version_compare($this->getServerVersion(), '4.1.8', '>=')) { - $this->markTestSkipped('Server rejects change streams that modify resume token (SERVER-37786)'); - } + $this->skipIfServerVersion('>=', '4.1.8', 'Server rejects change streams that modify resume token (SERVER-37786)'); $pipeline = [['$project' => ['_id' => ['$literal' => 'foo']]]]; @@ -773,11 +761,9 @@ public function testResumeTokenInvalidTypeClientSideError() * response is missing the resume token (if wire version is < 8, this is a * driver-side error; for 8+, this is a server-side error)" */ - public function testResumeTokenInvalidTypeServerSideError() + public function testResumeTokenInvalidTypeServerSideError(): void { - if (version_compare($this->getServerVersion(), '4.1.8', '<')) { - $this->markTestSkipped('Server does not reject change streams that modify resume token'); - } + $this->skipIfServerVersion('<', '4.1.8', 'Server does not reject change streams that modify resume token'); $pipeline = [['$project' => ['_id' => ['$literal' => 'foo']]]]; @@ -791,7 +777,7 @@ public function testResumeTokenInvalidTypeServerSideError() $this->advanceCursorUntilValid($changeStream); } - public function testMaxAwaitTimeMS() + public function testMaxAwaitTimeMS(): void { /* On average, an acknowledged write takes about 20 ms to appear in a * change stream on the server so we'll use a higher maxAwaitTimeMS to @@ -857,7 +843,7 @@ public function testMaxAwaitTimeMS() } } - public function testRewindExtractsResumeTokenAndNextResumes() + public function testRewindExtractsResumeTokenAndNextResumes(): void { $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -917,7 +903,7 @@ public function testRewindExtractsResumeTokenAndNextResumes() $this->assertMatchesDocument($expectedResult, $changeStream->current()); } - public function testResumeAfterOption() + public function testResumeAfterOption(): void { $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -959,11 +945,9 @@ public function testResumeAfterOption() $this->assertMatchesDocument($expectedResult, $changeStream->current()); } - public function testStartAfterOption() + public function testStartAfterOption(): void { - if (version_compare($this->getServerVersion(), '4.1.1', '<')) { - $this->markTestSkipped('startAfter is not supported'); - } + $this->skipIfServerVersion('<', '4.1.1', 'startAfter is not supported'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -1005,10 +989,8 @@ public function testStartAfterOption() $this->assertMatchesDocument($expectedResult, $changeStream->current()); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedChangeDocument - */ - public function testTypeMapOption(array $typeMap, $expectedChangeDocument) + /** @dataProvider provideTypeMapOptionsAndExpectedChangeDocument */ + public function testTypeMapOption(array $typeMap, $expectedChangeDocument): void { $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], ['typeMap' => $typeMap] + $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -1057,7 +1039,7 @@ public function provideTypeMapOptionsAndExpectedChangeDocument() ]; } - public function testNextAdvancesKey() + public function testNextAdvancesKey(): void { $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -1076,9 +1058,9 @@ public function testNextAdvancesKey() $this->assertSame(1, $changeStream->key()); } - public function testResumeTokenNotFoundDoesNotAdvanceKey() + public function testResumeTokenNotFoundDoesNotAdvanceKey(): void { - $pipeline = [['$project' => ['_id' => 0 ]]]; + $pipeline = [['$project' => ['_id' => 0]]]; $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $pipeline, $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -1106,15 +1088,14 @@ public function testResumeTokenNotFoundDoesNotAdvanceKey() try { $changeStream->next(); $this->fail('Exception for missing resume token was not thrown'); - } catch (ResumeTokenException $e) { - } catch (ServerException $e) { + } catch (ResumeTokenException | ServerException $e) { } $this->assertFalse($changeStream->valid()); $this->assertNull($changeStream->key()); } - public function testSessionPersistsAfterResume() + public function testSessionPersistsAfterResume(): void { $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); @@ -1129,10 +1110,10 @@ public function testSessionPersistsAfterResume() * aggregate matches the lsid of any subsequent aggregates and getMores. */ (new CommandObserver())->observe( - function () use ($operation, &$changeStream) { + function () use ($operation, &$changeStream): void { $changeStream = $operation->execute($this->getPrimaryServer()); }, - function (array $event) use (&$originalSession) { + function (array $event) use (&$originalSession): void { $command = $event['started']->getCommand(); if (isset($command->aggregate)) { $originalSession = bin2hex((string) $command->lsid->id); @@ -1144,10 +1125,10 @@ function (array $event) use (&$originalSession) { $this->forceChangeStreamResume(); (new CommandObserver())->observe( - function () use (&$changeStream) { + function () use (&$changeStream): void { $changeStream->next(); }, - function (array $event) use (&$sessionAfterResume, &$commands) { + function (array $event) use (&$sessionAfterResume, &$commands): void { $commands[] = $event['started']->getCommandName(); $sessionAfterResume[] = bin2hex((string) $event['started']->getCommand()->lsid->id); } @@ -1173,8 +1154,12 @@ function (array $event) use (&$sessionAfterResume, &$commands) { } } - public function testSessionFreed() + public function testSessionFreed(): void { + if ($this->isShardedCluster()) { + $this->skipIfServerVersion('>=', '5.1.0', 'mongos still reports non-zero cursor ID for invalidated change stream (SERVER-60764)'); + } + $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -1185,7 +1170,7 @@ public function testSessionFreed() $this->assertIsCallable($rp->getValue($changeStream)); // Invalidate the cursor to verify that resumeCallable is unset when the cursor is exhausted. - $this->dropCollection(); + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); $this->advanceCursorUntilValid($changeStream); @@ -1194,10 +1179,10 @@ public function testSessionFreed() /** * Prose test 3: "ChangeStream will automatically resume one time on a - * resumable error (including not master) with the initial pipeline and + * resumable error (including not primary) with the initial pipeline and * options, except for the addition/update of a resumeToken." */ - public function testResumeRepeatsOriginalPipelineAndOptions() + public function testResumeRepeatsOriginalPipelineAndOptions(): void { $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); @@ -1208,20 +1193,20 @@ public function testResumeRepeatsOriginalPipelineAndOptions() 'mode' => ['times' => 1], 'data' => [ 'failCommands' => ['getMore'], - 'errorCode' => self::NOT_MASTER, + 'errorCode' => self::NOT_PRIMARY, 'errorLabels' => ['ResumableChangeStreamError'], ], ]); (new CommandObserver())->observe( - function () use ($operation) { + function () use ($operation): void { $changeStream = $operation->execute($this->getPrimaryServer()); // The first next will hit the fail point, causing a resume $changeStream->next(); $changeStream->next(); }, - function (array $event) use (&$aggregateCommands) { + function (array $event) use (&$aggregateCommands): void { $command = $event['started']->getCommand(); if ($event['started']->getCommandName() !== 'aggregate') { return; @@ -1277,11 +1262,9 @@ function (array $aggregateCommand) { * Prose test 4: "ChangeStream will not attempt to resume on any error * encountered while executing an aggregate command." */ - public function testErrorDuringAggregateCommandDoesNotCauseResume() + public function testErrorDuringAggregateCommandDoesNotCauseResume(): void { - if (version_compare($this->getServerVersion(), '4.0.0', '<')) { - $this->markTestSkipped('failCommand is not supported'); - } + $this->skipIfServerVersion('<', '4.0.0', 'failCommand is not supported'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); @@ -1296,10 +1279,10 @@ public function testErrorDuringAggregateCommandDoesNotCauseResume() $this->expectException(CommandException::class); (new CommandObserver())->observe( - function () use ($operation) { + function () use ($operation): void { $operation->execute($this->getPrimaryServer()); }, - function (array $event) use (&$commandCount) { + function (array $event) use (&$commandCount): void { $commandCount++; } ); @@ -1311,13 +1294,13 @@ function (array $event) use (&$commandCount) { * Prose test 6: "ChangeStream will perform server selection before * attempting to resume, using initial readPreference" */ - public function testOriginalReadPreferenceIsPreservedOnResume() + public function testOriginalReadPreferenceIsPreservedOnResume(): void { if ($this->isShardedCluster()) { $this->markTestSkipped('Test does not apply to sharded clusters'); } - $readPreference = new ReadPreference('secondary'); + $readPreference = new ReadPreference(ReadPreference::SECONDARY); $options = ['readPreference' => $readPreference] + $this->defaultOptions; $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); @@ -1341,8 +1324,8 @@ function () { $changeStream, ChangeStream::class ); - /** @var Cursor $cursor */ $cursor = $getCursor(); + assert($cursor instanceof Cursor); self::assertTrue($cursor->getServer()->isSecondary()); } @@ -1356,11 +1339,9 @@ function () { * - getResumeToken must return resumeAfter from the initial aggregate if the option was specified. * - If resumeAfter was not specified, the getResumeToken result must be empty. */ - public function testGetResumeTokenReturnsOriginalResumeTokenOnEmptyBatch() + public function testGetResumeTokenReturnsOriginalResumeTokenOnEmptyBatch(): void { - if ($this->isPostBatchResumeTokenSupported()) { - $this->markTestSkipped('postBatchResumeToken is supported'); - } + $this->skipIfServerVersion('>=', '4.0.7', 'postBatchResumeToken is supported'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -1392,12 +1373,9 @@ public function testGetResumeTokenReturnsOriginalResumeTokenOnEmptyBatch() * - getResumeToken must return resumeAfter from the initial aggregate if the option was specified. * - If neither the startAfter nor resumeAfter options were specified, the getResumeToken result must be empty. */ - public function testResumeTokenBehaviour() + public function testResumeTokenBehaviour(): void { - if (version_compare($this->getServerVersion(), '4.1.1', '<')) { - $this->markTestSkipped('Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'); - } - + $this->skipIfServerVersion('<', '4.1.1', 'Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'); $this->skipIfIsShardedCluster('Resume token behaviour can\'t be reliably tested on sharded clusters.'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); @@ -1405,9 +1383,9 @@ public function testResumeTokenBehaviour() $lastOpTime = null; $changeStream = null; - (new CommandObserver())->observe(function () use ($operation, &$changeStream) { + (new CommandObserver())->observe(function () use ($operation, &$changeStream): void { $changeStream = $operation->execute($this->getPrimaryServer()); - }, function ($event) use (&$lastOpTime) { + }, function ($event) use (&$lastOpTime): void { $this->assertInstanceOf(CommandSucceededEvent::class, $event['succeeded']); $reply = $event['succeeded']->getReply(); @@ -1451,11 +1429,9 @@ public function testResumeTokenBehaviour() * MUST include a startAfter option and MUST NOT include a resumeAfter * option when resuming a change stream." */ - public function testResumingChangeStreamWithoutPreviousResultsIncludesStartAfterOption() + public function testResumingChangeStreamWithoutPreviousResultsIncludesStartAfterOption(): void { - if (version_compare($this->getServerVersion(), '4.1.1', '<')) { - $this->markTestSkipped('Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'); - } + $this->skipIfServerVersion('<', '4.1.1', 'Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -1475,10 +1451,10 @@ public function testResumingChangeStreamWithoutPreviousResultsIncludesStartAfter $aggregateCommand = null; (new CommandObserver())->observe( - function () use ($changeStream) { + function () use ($changeStream): void { $changeStream->next(); }, - function (array $event) use (&$aggregateCommand) { + function (array $event) use (&$aggregateCommand): void { if ($event['started']->getCommandName() !== 'aggregate') { return; } @@ -1498,11 +1474,9 @@ function (array $event) use (&$aggregateCommand) { * MUST include a resumeAfter option and MUST NOT include a startAfter * option when resuming a change stream." */ - public function testResumingChangeStreamWithPreviousResultsIncludesResumeAfterOption() + public function testResumingChangeStreamWithPreviousResultsIncludesResumeAfterOption(): void { - if (version_compare($this->getServerVersion(), '4.1.1', '<')) { - $this->markTestSkipped('Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'); - } + $this->skipIfServerVersion('<', '4.1.1', 'Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -1526,10 +1500,10 @@ public function testResumingChangeStreamWithPreviousResultsIncludesResumeAfterOp $aggregateCommand = null; (new CommandObserver())->observe( - function () use ($changeStream) { + function () use ($changeStream): void { $changeStream->next(); }, - function (array $event) use (&$aggregateCommand) { + function (array $event) use (&$aggregateCommand): void { if ($event['started']->getCommandName() !== 'aggregate') { return; } @@ -1543,13 +1517,13 @@ function (array $event) use (&$aggregateCommand) { $this->assertObjectHasAttribute('resumeAfter', $aggregateCommand->pipeline[0]->{'$changeStream'}); } - private function assertNoCommandExecuted(callable $callable) + private function assertNoCommandExecuted(callable $callable): void { $commands = []; (new CommandObserver())->observe( $callable, - function (array $event) use (&$commands) { + function (array $event) use (&$commands): void { $this->fail(sprintf('"%s" command was executed', $event['started']->getCommandName())); } ); @@ -1557,14 +1531,14 @@ function (array $event) use (&$commands) { $this->assertEmpty($commands); } - private function forceChangeStreamResume() + private function forceChangeStreamResume(): void { $this->configureFailPoint([ 'configureFailPoint' => 'failCommand', 'mode' => ['times' => 1], 'data' => [ 'failCommands' => ['getMore'], - 'errorCode' => self::NOT_MASTER, + 'errorCode' => self::NOT_PRIMARY, 'errorLabels' => ['ResumableChangeStreamError'], ], ]); @@ -1580,7 +1554,7 @@ private function getPostBatchResumeTokenFromReply(stdClass $reply) return $reply->cursor->postBatchResumeToken; } - private function insertDocument($document) + private function insertDocument($document): void { $insertOne = new InsertOne( $this->getDatabaseName(), @@ -1592,17 +1566,12 @@ private function insertDocument($document) $this->assertEquals(1, $writeResult->getInsertedCount()); } - private function isPostBatchResumeTokenSupported() - { - return version_compare($this->getServerVersion(), '4.0.7', '>='); - } - private function isStartAtOperationTimeSupported() { return server_supports_feature($this->getPrimaryServer(), self::$wireVersionForStartAtOperationTime); } - private function advanceCursorUntilValid(Iterator $iterator, $limitOnShardedClusters = 10) + private function advanceCursorUntilValid(Iterator $iterator, $limitOnShardedClusters = 10): void { if (! $this->isShardedCluster()) { $iterator->next(); @@ -1621,7 +1590,7 @@ private function advanceCursorUntilValid(Iterator $iterator, $limitOnShardedClus throw new ExpectationFailedException(sprintf('Expected cursor to return an element but none was found after %d attempts.', $limitOnShardedClusters)); } - private function skipIfIsShardedCluster($message) + private function skipIfIsShardedCluster($message): void { if (! $this->isShardedCluster()) { return; diff --git a/tests/Operation/WatchTest.php b/tests/Operation/WatchTest.php index 8cf94254a..5c07d8d18 100644 --- a/tests/Operation/WatchTest.php +++ b/tests/Operation/WatchTest.php @@ -12,7 +12,7 @@ */ class WatchTest extends FunctionalTestCase { - public function testConstructorCollectionNameShouldBeNullIfDatabaseNameIsNull() + public function testConstructorCollectionNameShouldBeNullIfDatabaseNameIsNull(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$collectionName should also be null if $databaseName is null'); @@ -20,10 +20,10 @@ public function testConstructorCollectionNameShouldBeNullIfDatabaseNameIsNull() new Watch($this->manager, null, 'foo', []); } - public function testConstructorPipelineArgumentMustBeAList() + public function testConstructorPipelineArgumentMustBeAList(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$pipeline is not a list (unexpected index: "foo")'); + $this->expectExceptionMessage('$pipeline is not a valid aggregation pipeline'); /* Note: Watch uses array_unshift() to prepend the $changeStream stage * to the pipeline. Since array_unshift() reindexes numeric keys, we'll @@ -31,10 +31,8 @@ public function testConstructorPipelineArgumentMustBeAList() new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), ['foo' => ['$match' => ['x' => 1]]]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options) + /** @dataProvider provideInvalidConstructorOptions */ + public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); @@ -56,6 +54,10 @@ public function provideInvalidConstructorOptions() $options[][] = ['fullDocument' => $value]; } + foreach ($this->getInvalidStringValues() as $value) { + $options[][] = ['fullDocumentBeforeChange' => $value]; + } + foreach ($this->getInvalidIntegerValues() as $value) { $options[][] = ['maxAwaitTimeMS' => $value]; } diff --git a/tests/PHPUnit/Functions.php b/tests/PHPUnit/Functions.php index 5d983e893..55edd3a6f 100644 --- a/tests/PHPUnit/Functions.php +++ b/tests/PHPUnit/Functions.php @@ -1,4 +1,5 @@ - $expected * @psalm-assert ExpectedType $actual */ - function assertInstanceOf(string $expected, $actual, string $message = '') + function assertInstanceOf(string $expected, $actual, string $message = ''): void { Assert::assertInstanceOf(...func_get_args()); } @@ -1348,7 +1347,7 @@ function assertInstanceOf(string $expected, $actual, string $message = '') * @psalm-param class-string $expected * @psalm-assert !ExpectedType $actual */ - function assertNotInstanceOf(string $expected, $actual, string $message = '') + function assertNotInstanceOf(string $expected, $actual, string $message = ''): void { Assert::assertNotInstanceOf(...func_get_args()); } @@ -1365,7 +1364,7 @@ function assertNotInstanceOf(string $expected, $actual, string $message = '') * * @psalm-assert array $actual */ - function assertIsArray($actual, string $message = '') + function assertIsArray($actual, string $message = ''): void { Assert::assertIsArray(...func_get_args()); } @@ -1382,7 +1381,7 @@ function assertIsArray($actual, string $message = '') * * @psalm-assert bool $actual */ - function assertIsBool($actual, string $message = '') + function assertIsBool($actual, string $message = ''): void { Assert::assertIsBool(...func_get_args()); } @@ -1399,7 +1398,7 @@ function assertIsBool($actual, string $message = '') * * @psalm-assert float $actual */ - function assertIsFloat($actual, string $message = '') + function assertIsFloat($actual, string $message = ''): void { Assert::assertIsFloat(...func_get_args()); } @@ -1416,7 +1415,7 @@ function assertIsFloat($actual, string $message = '') * * @psalm-assert int $actual */ - function assertIsInt($actual, string $message = '') + function assertIsInt($actual, string $message = ''): void { Assert::assertIsInt(...func_get_args()); } @@ -1433,7 +1432,7 @@ function assertIsInt($actual, string $message = '') * * @psalm-assert numeric $actual */ - function assertIsNumeric($actual, string $message = '') + function assertIsNumeric($actual, string $message = ''): void { Assert::assertIsNumeric(...func_get_args()); } @@ -1450,7 +1449,7 @@ function assertIsNumeric($actual, string $message = '') * * @psalm-assert object $actual */ - function assertIsObject($actual, string $message = '') + function assertIsObject($actual, string $message = ''): void { Assert::assertIsObject(...func_get_args()); } @@ -1467,7 +1466,7 @@ function assertIsObject($actual, string $message = '') * * @psalm-assert resource $actual */ - function assertIsResource($actual, string $message = '') + function assertIsResource($actual, string $message = ''): void { Assert::assertIsResource(...func_get_args()); } @@ -1484,7 +1483,7 @@ function assertIsResource($actual, string $message = '') * * @psalm-assert resource $actual */ - function assertIsClosedResource($actual, string $message = '') + function assertIsClosedResource($actual, string $message = ''): void { Assert::assertIsClosedResource(...func_get_args()); } @@ -1501,7 +1500,7 @@ function assertIsClosedResource($actual, string $message = '') * * @psalm-assert string $actual */ - function assertIsString($actual, string $message = '') + function assertIsString($actual, string $message = ''): void { Assert::assertIsString(...func_get_args()); } @@ -1518,7 +1517,7 @@ function assertIsString($actual, string $message = '') * * @psalm-assert scalar $actual */ - function assertIsScalar($actual, string $message = '') + function assertIsScalar($actual, string $message = ''): void { Assert::assertIsScalar(...func_get_args()); } @@ -1535,7 +1534,7 @@ function assertIsScalar($actual, string $message = '') * * @psalm-assert callable $actual */ - function assertIsCallable($actual, string $message = '') + function assertIsCallable($actual, string $message = ''): void { Assert::assertIsCallable(...func_get_args()); } @@ -1552,7 +1551,7 @@ function assertIsCallable($actual, string $message = '') * * @psalm-assert iterable $actual */ - function assertIsIterable($actual, string $message = '') + function assertIsIterable($actual, string $message = ''): void { Assert::assertIsIterable(...func_get_args()); } @@ -1569,7 +1568,7 @@ function assertIsIterable($actual, string $message = '') * * @psalm-assert !array $actual */ - function assertIsNotArray($actual, string $message = '') + function assertIsNotArray($actual, string $message = ''): void { Assert::assertIsNotArray(...func_get_args()); } @@ -1586,7 +1585,7 @@ function assertIsNotArray($actual, string $message = '') * * @psalm-assert !bool $actual */ - function assertIsNotBool($actual, string $message = '') + function assertIsNotBool($actual, string $message = ''): void { Assert::assertIsNotBool(...func_get_args()); } @@ -1603,7 +1602,7 @@ function assertIsNotBool($actual, string $message = '') * * @psalm-assert !float $actual */ - function assertIsNotFloat($actual, string $message = '') + function assertIsNotFloat($actual, string $message = ''): void { Assert::assertIsNotFloat(...func_get_args()); } @@ -1620,7 +1619,7 @@ function assertIsNotFloat($actual, string $message = '') * * @psalm-assert !int $actual */ - function assertIsNotInt($actual, string $message = '') + function assertIsNotInt($actual, string $message = ''): void { Assert::assertIsNotInt(...func_get_args()); } @@ -1637,7 +1636,7 @@ function assertIsNotInt($actual, string $message = '') * * @psalm-assert !numeric $actual */ - function assertIsNotNumeric($actual, string $message = '') + function assertIsNotNumeric($actual, string $message = ''): void { Assert::assertIsNotNumeric(...func_get_args()); } @@ -1654,7 +1653,7 @@ function assertIsNotNumeric($actual, string $message = '') * * @psalm-assert !object $actual */ - function assertIsNotObject($actual, string $message = '') + function assertIsNotObject($actual, string $message = ''): void { Assert::assertIsNotObject(...func_get_args()); } @@ -1671,7 +1670,7 @@ function assertIsNotObject($actual, string $message = '') * * @psalm-assert !resource $actual */ - function assertIsNotResource($actual, string $message = '') + function assertIsNotResource($actual, string $message = ''): void { Assert::assertIsNotResource(...func_get_args()); } @@ -1688,7 +1687,7 @@ function assertIsNotResource($actual, string $message = '') * * @psalm-assert !resource $actual */ - function assertIsNotClosedResource($actual, string $message = '') + function assertIsNotClosedResource($actual, string $message = ''): void { Assert::assertIsNotClosedResource(...func_get_args()); } @@ -1705,7 +1704,7 @@ function assertIsNotClosedResource($actual, string $message = '') * * @psalm-assert !string $actual */ - function assertIsNotString($actual, string $message = '') + function assertIsNotString($actual, string $message = ''): void { Assert::assertIsNotString(...func_get_args()); } @@ -1722,7 +1721,7 @@ function assertIsNotString($actual, string $message = '') * * @psalm-assert !scalar $actual */ - function assertIsNotScalar($actual, string $message = '') + function assertIsNotScalar($actual, string $message = ''): void { Assert::assertIsNotScalar(...func_get_args()); } @@ -1739,7 +1738,7 @@ function assertIsNotScalar($actual, string $message = '') * * @psalm-assert !callable $actual */ - function assertIsNotCallable($actual, string $message = '') + function assertIsNotCallable($actual, string $message = ''): void { Assert::assertIsNotCallable(...func_get_args()); } @@ -1756,7 +1755,7 @@ function assertIsNotCallable($actual, string $message = '') * * @psalm-assert !iterable $actual */ - function assertIsNotIterable($actual, string $message = '') + function assertIsNotIterable($actual, string $message = ''): void { Assert::assertIsNotIterable(...func_get_args()); } @@ -1771,7 +1770,7 @@ function assertIsNotIterable($actual, string $message = '') * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertMatchesRegularExpression(string $pattern, string $string, string $message = '') + function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void { Assert::assertMatchesRegularExpression(...func_get_args()); } @@ -1789,7 +1788,7 @@ function assertMatchesRegularExpression(string $pattern, string $string, string * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4086 * @see Assert::assertRegExp */ - function assertRegExp(string $pattern, string $string, string $message = '') + function assertRegExp(string $pattern, string $string, string $message = ''): void { Assert::assertRegExp(...func_get_args()); } @@ -1804,7 +1803,7 @@ function assertRegExp(string $pattern, string $string, string $message = '') * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = '') + function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = ''): void { Assert::assertDoesNotMatchRegularExpression(...func_get_args()); } @@ -1822,7 +1821,7 @@ function assertDoesNotMatchRegularExpression(string $pattern, string $string, st * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4089 * @see Assert::assertNotRegExp */ - function assertNotRegExp(string $pattern, string $string, string $message = '') + function assertNotRegExp(string $pattern, string $string, string $message = ''): void { Assert::assertNotRegExp(...func_get_args()); } @@ -1842,7 +1841,7 @@ function assertNotRegExp(string $pattern, string $string, string $message = '') * @throws InvalidArgumentException * @throws Exception */ - function assertSameSize($expected, $actual, string $message = '') + function assertSameSize($expected, $actual, string $message = ''): void { Assert::assertSameSize(...func_get_args()); } @@ -1862,7 +1861,7 @@ function assertSameSize($expected, $actual, string $message = '') * @throws InvalidArgumentException * @throws Exception */ - function assertNotSameSize($expected, $actual, string $message = '') + function assertNotSameSize($expected, $actual, string $message = ''): void { Assert::assertNotSameSize(...func_get_args()); } @@ -1877,7 +1876,7 @@ function assertNotSameSize($expected, $actual, string $message = '') * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringMatchesFormat(string $format, string $string, string $message = '') + function assertStringMatchesFormat(string $format, string $string, string $message = ''): void { Assert::assertStringMatchesFormat(...func_get_args()); } @@ -1892,7 +1891,7 @@ function assertStringMatchesFormat(string $format, string $string, string $messa * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringNotMatchesFormat(string $format, string $string, string $message = '') + function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void { Assert::assertStringNotMatchesFormat(...func_get_args()); } @@ -1907,7 +1906,7 @@ function assertStringNotMatchesFormat(string $format, string $string, string $me * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = '') + function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void { Assert::assertStringMatchesFormatFile(...func_get_args()); } @@ -1922,7 +1921,7 @@ function assertStringMatchesFormatFile(string $formatFile, string $string, strin * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = '') + function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void { Assert::assertStringNotMatchesFormatFile(...func_get_args()); } @@ -1937,7 +1936,7 @@ function assertStringNotMatchesFormatFile(string $formatFile, string $string, st * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringStartsWith(string $prefix, string $string, string $message = '') + function assertStringStartsWith(string $prefix, string $string, string $message = ''): void { Assert::assertStringStartsWith(...func_get_args()); } @@ -1949,13 +1948,10 @@ function assertStringStartsWith(string $prefix, string $string, string $message * * @see Assert::assertStringStartsNotWith * - * @param string $prefix - * @param string $string - * * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringStartsNotWith($prefix, $string, string $message = '') + function assertStringStartsNotWith(string $prefix, string $string, string $message = ''): void { Assert::assertStringStartsNotWith(...func_get_args()); } @@ -1968,7 +1964,7 @@ function assertStringStartsNotWith($prefix, $string, string $message = '') * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringContainsString(string $needle, string $haystack, string $message = '') + function assertStringContainsString(string $needle, string $haystack, string $message = ''): void { Assert::assertStringContainsString(...func_get_args()); } @@ -1981,7 +1977,7 @@ function assertStringContainsString(string $needle, string $haystack, string $me * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') + function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void { Assert::assertStringContainsStringIgnoringCase(...func_get_args()); } @@ -1994,7 +1990,7 @@ function assertStringContainsStringIgnoringCase(string $needle, string $haystack * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringNotContainsString(string $needle, string $haystack, string $message = '') + function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void { Assert::assertStringNotContainsString(...func_get_args()); } @@ -2007,7 +2003,7 @@ function assertStringNotContainsString(string $needle, string $haystack, string * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = '') + function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void { Assert::assertStringNotContainsStringIgnoringCase(...func_get_args()); } @@ -2022,7 +2018,7 @@ function assertStringNotContainsStringIgnoringCase(string $needle, string $hayst * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringEndsWith(string $suffix, string $string, string $message = '') + function assertStringEndsWith(string $suffix, string $string, string $message = ''): void { Assert::assertStringEndsWith(...func_get_args()); } @@ -2037,7 +2033,7 @@ function assertStringEndsWith(string $suffix, string $string, string $message = * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertStringEndsNotWith(string $suffix, string $string, string $message = '') + function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void { Assert::assertStringEndsNotWith(...func_get_args()); } @@ -2053,7 +2049,7 @@ function assertStringEndsNotWith(string $suffix, string $string, string $message * @throws InvalidArgumentException * @throws Exception */ - function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') + function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void { Assert::assertXmlFileEqualsXmlFile(...func_get_args()); } @@ -2069,7 +2065,7 @@ function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, st * @throws InvalidArgumentException * @throws Exception */ - function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = '') + function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void { Assert::assertXmlFileNotEqualsXmlFile(...func_get_args()); } @@ -2087,7 +2083,7 @@ function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, * @throws InvalidArgumentException * @throws XmlException */ - function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') + function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void { Assert::assertXmlStringEqualsXmlFile(...func_get_args()); } @@ -2105,7 +2101,7 @@ function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $ * @throws InvalidArgumentException * @throws XmlException */ - function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = '') + function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void { Assert::assertXmlStringNotEqualsXmlFile(...func_get_args()); } @@ -2124,7 +2120,7 @@ function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, strin * @throws InvalidArgumentException * @throws XmlException */ - function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = '') + function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = ''): void { Assert::assertXmlStringEqualsXmlString(...func_get_args()); } @@ -2143,7 +2139,7 @@ function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $messag * @throws InvalidArgumentException * @throws XmlException */ - function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = '') + function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = ''): void { Assert::assertXmlStringNotEqualsXmlString(...func_get_args()); } @@ -2162,7 +2158,7 @@ function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $mes * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4091 * @see Assert::assertEqualXMLStructure */ - function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = '') + function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = ''): void { Assert::assertEqualXMLStructure(...func_get_args()); } @@ -2177,7 +2173,7 @@ function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actual * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertThat($value, Constraint $constraint, string $message = '') + function assertThat($value, Constraint $constraint, string $message = ''): void { Assert::assertThat(...func_get_args()); } @@ -2192,7 +2188,7 @@ function assertThat($value, Constraint $constraint, string $message = '') * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertJson(string $actualJson, string $message = '') + function assertJson(string $actualJson, string $message = ''): void { Assert::assertJson(...func_get_args()); } @@ -2207,7 +2203,7 @@ function assertJson(string $actualJson, string $message = '') * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '') + function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void { Assert::assertJsonStringEqualsJsonString(...func_get_args()); } @@ -2219,13 +2215,10 @@ function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJs * * @see Assert::assertJsonStringNotEqualsJsonString * - * @param string $expectedJson - * @param string $actualJson - * * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = '') + function assertJsonStringNotEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void { Assert::assertJsonStringNotEqualsJsonString(...func_get_args()); } @@ -2240,7 +2233,7 @@ function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') + function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void { Assert::assertJsonStringEqualsJsonFile(...func_get_args()); } @@ -2255,7 +2248,7 @@ function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = '') + function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void { Assert::assertJsonStringNotEqualsJsonFile(...func_get_args()); } @@ -2270,7 +2263,7 @@ function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJ * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') + function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void { Assert::assertJsonFileEqualsJsonFile(...func_get_args()); } @@ -2285,308 +2278,308 @@ function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, * @throws ExpectationFailedException * @throws InvalidArgumentException */ - function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = '') + function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void { Assert::assertJsonFileNotEqualsJsonFile(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\logicalAnd')) { - function logicalAnd() : LogicalAnd + function logicalAnd(): LogicalAnd { return Assert::logicalAnd(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\logicalOr')) { - function logicalOr() : LogicalOr + function logicalOr(): LogicalOr { return Assert::logicalOr(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\logicalNot')) { - function logicalNot(Constraint $constraint) : LogicalNot + function logicalNot(Constraint $constraint): LogicalNot { return Assert::logicalNot(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\logicalXor')) { - function logicalXor() : LogicalXor + function logicalXor(): LogicalXor { return Assert::logicalXor(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\anything')) { - function anything() : IsAnything + function anything(): IsAnything { return Assert::anything(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isTrue')) { - function isTrue() : IsTrue + function isTrue(): IsTrue { return Assert::isTrue(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\callback')) { - function callback(callable $callback) : Callback + function callback(callable $callback): Callback { return Assert::callback(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isFalse')) { - function isFalse() : IsFalse + function isFalse(): IsFalse { return Assert::isFalse(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isJson')) { - function isJson() : IsJson + function isJson(): IsJson { return Assert::isJson(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isNull')) { - function isNull() : IsNull + function isNull(): IsNull { return Assert::isNull(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isFinite')) { - function isFinite() : IsFinite + function isFinite(): IsFinite { return Assert::isFinite(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isInfinite')) { - function isInfinite() : IsInfinite + function isInfinite(): IsInfinite { return Assert::isInfinite(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isNan')) { - function isNan() : IsNan + function isNan(): IsNan { return Assert::isNan(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\containsEqual')) { - function containsEqual($value) : TraversableContainsEqual + function containsEqual($value): TraversableContainsEqual { return Assert::containsEqual(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\containsIdentical')) { - function containsIdentical($value) : TraversableContainsIdentical + function containsIdentical($value): TraversableContainsIdentical { return Assert::containsIdentical(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\containsOnly')) { - function containsOnly(string $type) : TraversableContainsOnly + function containsOnly(string $type): TraversableContainsOnly { return Assert::containsOnly(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\containsOnlyInstancesOf')) { - function containsOnlyInstancesOf(string $className) : TraversableContainsOnly + function containsOnlyInstancesOf(string $className): TraversableContainsOnly { return Assert::containsOnlyInstancesOf(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\arrayHasKey')) { - function arrayHasKey($key) : ArrayHasKey + function arrayHasKey($key): ArrayHasKey { return Assert::arrayHasKey(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\equalTo')) { - function equalTo($value) : IsEqual + function equalTo($value): IsEqual { return Assert::equalTo(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\equalToCanonicalizing')) { - function equalToCanonicalizing($value) : IsEqualCanonicalizing + function equalToCanonicalizing($value): IsEqualCanonicalizing { return Assert::equalToCanonicalizing(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\equalToIgnoringCase')) { - function equalToIgnoringCase($value) : IsEqualIgnoringCase + function equalToIgnoringCase($value): IsEqualIgnoringCase { return Assert::equalToIgnoringCase(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\equalToWithDelta')) { - function equalToWithDelta($value, float $delta) : IsEqualWithDelta + function equalToWithDelta($value, float $delta): IsEqualWithDelta { return Assert::equalToWithDelta(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isEmpty')) { - function isEmpty() : IsEmpty + function isEmpty(): IsEmpty { return Assert::isEmpty(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isWritable')) { - function isWritable() : IsWritable + function isWritable(): IsWritable { return Assert::isWritable(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isReadable')) { - function isReadable() : IsReadable + function isReadable(): IsReadable { return Assert::isReadable(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\directoryExists')) { - function directoryExists() : DirectoryExists + function directoryExists(): DirectoryExists { return Assert::directoryExists(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\fileExists')) { - function fileExists() : FileExists + function fileExists(): FileExists { return Assert::fileExists(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\greaterThan')) { - function greaterThan($value) : GreaterThan + function greaterThan($value): GreaterThan { return Assert::greaterThan(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\greaterThanOrEqual')) { - function greaterThanOrEqual($value) : LogicalOr + function greaterThanOrEqual($value): LogicalOr { return Assert::greaterThanOrEqual(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\classHasAttribute')) { - function classHasAttribute(string $attributeName) : ClassHasAttribute + function classHasAttribute(string $attributeName): ClassHasAttribute { return Assert::classHasAttribute(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\classHasStaticAttribute')) { - function classHasStaticAttribute(string $attributeName) : ClassHasStaticAttribute + function classHasStaticAttribute(string $attributeName): ClassHasStaticAttribute { return Assert::classHasStaticAttribute(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\objectHasAttribute')) { - function objectHasAttribute($attributeName) : ObjectHasAttribute + function objectHasAttribute($attributeName): ObjectHasAttribute { return Assert::objectHasAttribute(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\identicalTo')) { - function identicalTo($value) : IsIdentical + function identicalTo($value): IsIdentical { return Assert::identicalTo(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isInstanceOf')) { - function isInstanceOf(string $className) : IsInstanceOf + function isInstanceOf(string $className): IsInstanceOf { return Assert::isInstanceOf(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\isType')) { - function isType(string $type) : IsType + function isType(string $type): IsType { return Assert::isType(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\lessThan')) { - function lessThan($value) : LessThan + function lessThan($value): LessThan { return Assert::lessThan(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\lessThanOrEqual')) { - function lessThanOrEqual($value) : LogicalOr + function lessThanOrEqual($value): LogicalOr { return Assert::lessThanOrEqual(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\matchesRegularExpression')) { - function matchesRegularExpression(string $pattern) : RegularExpression + function matchesRegularExpression(string $pattern): RegularExpression { return Assert::matchesRegularExpression(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\matches')) { - function matches(string $string) : StringMatchesFormatDescription + function matches(string $string): StringMatchesFormatDescription { return Assert::matches(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\stringStartsWith')) { - function stringStartsWith($prefix) : StringStartsWith + function stringStartsWith($prefix): StringStartsWith { return Assert::stringStartsWith(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\stringContains')) { - function stringContains(string $string, bool $case = true) : StringContains + function stringContains(string $string, bool $case = true): StringContains { return Assert::stringContains(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\stringEndsWith')) { - function stringEndsWith(string $suffix) : StringEndsWith + function stringEndsWith(string $suffix): StringEndsWith { return Assert::stringEndsWith(...func_get_args()); } } if (! function_exists('PHPUnit\Framework\countOf')) { - function countOf(int $count) : Count + function countOf(int $count): Count { return Assert::countOf(...func_get_args()); } @@ -2597,7 +2590,7 @@ function countOf(int $count) : Count * Returns a matcher that matches when the method is executed * zero or more times. */ - function any() : AnyInvokedCountMatcher + function any(): AnyInvokedCountMatcher { return new AnyInvokedCountMatcher(); } @@ -2607,7 +2600,7 @@ function any() : AnyInvokedCountMatcher /** * Returns a matcher that matches when the method is never executed. */ - function never() : InvokedCountMatcher + function never(): InvokedCountMatcher { return new InvokedCountMatcher(0); } @@ -2618,7 +2611,7 @@ function never() : InvokedCountMatcher * Returns a matcher that matches when the method is executed * at least N times. */ - function atLeast(int $requiredInvocations) : InvokedAtLeastCountMatcher + function atLeast(int $requiredInvocations): InvokedAtLeastCountMatcher { return new InvokedAtLeastCountMatcher( $requiredInvocations @@ -2630,7 +2623,7 @@ function atLeast(int $requiredInvocations) : InvokedAtLeastCountMatcher /** * Returns a matcher that matches when the method is executed at least once. */ - function atLeastOnce() : InvokedAtLeastOnceMatcher + function atLeastOnce(): InvokedAtLeastOnceMatcher { return new InvokedAtLeastOnceMatcher(); } @@ -2640,7 +2633,7 @@ function atLeastOnce() : InvokedAtLeastOnceMatcher /** * Returns a matcher that matches when the method is executed exactly once. */ - function once() : InvokedCountMatcher + function once(): InvokedCountMatcher { return new InvokedCountMatcher(1); } @@ -2651,7 +2644,7 @@ function once() : InvokedCountMatcher * Returns a matcher that matches when the method is executed * exactly $count times. */ - function exactly(int $count) : InvokedCountMatcher + function exactly(int $count): InvokedCountMatcher { return new InvokedCountMatcher($count); } @@ -2662,7 +2655,7 @@ function exactly(int $count) : InvokedCountMatcher * Returns a matcher that matches when the method is executed * at most N times. */ - function atMost(int $allowedInvocations) : InvokedAtMostCountMatcher + function atMost(int $allowedInvocations): InvokedAtMostCountMatcher { return new InvokedAtMostCountMatcher($allowedInvocations); } @@ -2673,35 +2666,35 @@ function atMost(int $allowedInvocations) : InvokedAtMostCountMatcher * Returns a matcher that matches when the method is executed * at the given index. */ - function at(int $index) : InvokedAtIndexMatcher + function at(int $index): InvokedAtIndexMatcher { return new InvokedAtIndexMatcher($index); } } if (! function_exists('PHPUnit\Framework\returnValue')) { - function returnValue($value) : ReturnStub + function returnValue($value): ReturnStub { return new ReturnStub($value); } } if (! function_exists('PHPUnit\Framework\returnValueMap')) { - function returnValueMap(array $valueMap) : ReturnValueMapStub + function returnValueMap(array $valueMap): ReturnValueMapStub { return new ReturnValueMapStub($valueMap); } } if (! function_exists('PHPUnit\Framework\returnArgument')) { - function returnArgument(int $argumentIndex) : ReturnArgumentStub + function returnArgument(int $argumentIndex): ReturnArgumentStub { return new ReturnArgumentStub($argumentIndex); } } if (! function_exists('PHPUnit\Framework\returnCallback')) { - function returnCallback($callback) : ReturnCallbackStub + function returnCallback($callback): ReturnCallbackStub { return new ReturnCallbackStub($callback); } @@ -2713,21 +2706,21 @@ function returnCallback($callback) : ReturnCallbackStub * * This method is useful when mocking a fluent interface. */ - function returnSelf() : ReturnSelfStub + function returnSelf(): ReturnSelfStub { return new ReturnSelfStub(); } } if (! function_exists('PHPUnit\Framework\throwException')) { - function throwException(Throwable $exception) : ExceptionStub + function throwException(Throwable $exception): ExceptionStub { return new ExceptionStub($exception); } } if (! function_exists('PHPUnit\Framework\onConsecutiveCalls')) { - function onConsecutiveCalls() : ConsecutiveCallsStub + function onConsecutiveCalls(): ConsecutiveCallsStub { $args = func_get_args(); diff --git a/tests/PedantryTest.php b/tests/PedantryTest.php index e9b3f3fed..2885d82a7 100644 --- a/tests/PedantryTest.php +++ b/tests/PedantryTest.php @@ -7,6 +7,7 @@ use ReflectionClass; use ReflectionMethod; use RegexIterator; + use function array_filter; use function array_map; use function realpath; @@ -15,6 +16,7 @@ use function strlen; use function substr; use function usort; + use const DIRECTORY_SEPARATOR; /** @@ -22,10 +24,8 @@ */ class PedantryTest extends TestCase { - /** - * @dataProvider provideProjectClassNames - */ - public function testMethodsAreOrderedAlphabeticallyByVisibility($className) + /** @dataProvider provideProjectClassNames */ + public function testMethodsAreOrderedAlphabeticallyByVisibility($className): void { $class = new ReflectionClass($className); $methods = $class->getMethods(); @@ -41,9 +41,11 @@ function (ReflectionMethod $method) use ($class) { if ($method->getModifiers() & ReflectionMethod::IS_PRIVATE) { return '2' . $method->getName(); } + if ($method->getModifiers() & ReflectionMethod::IS_PROTECTED) { return '1' . $method->getName(); } + if ($method->getModifiers() & ReflectionMethod::IS_PUBLIC) { return '0' . $method->getName(); } diff --git a/tests/SpecTests/AtlasDataLakeSpecTest.php b/tests/SpecTests/AtlasDataLakeSpecTest.php index 1e4fda722..4b37a2f12 100644 --- a/tests/SpecTests/AtlasDataLakeSpecTest.php +++ b/tests/SpecTests/AtlasDataLakeSpecTest.php @@ -2,12 +2,11 @@ namespace MongoDB\Tests\SpecTests; -use MongoDB\Client; use MongoDB\Driver\Command; use MongoDB\Driver\Cursor; use MongoDB\Tests\CommandObserver; use stdClass; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; + use function basename; use function current; use function explode; @@ -22,9 +21,7 @@ */ class AtlasDataLakeSpecTest extends FunctionalTestCase { - use SetUpTearDownTrait; - - private function doSetUp() + public function setUp(): void { parent::setUp(); @@ -39,7 +36,7 @@ private function doSetUp() * @param stdClass $expected Expected command document * @param stdClass $actual Actual command document */ - public static function assertCommandMatches(stdClass $expected, stdClass $actual) + public static function assertCommandMatches(stdClass $expected, stdClass $actual): void { foreach ($expected as $key => $value) { if ($value === null) { @@ -61,7 +58,7 @@ public static function assertCommandMatches(stdClass $expected, stdClass $actual * @param string $databaseName Name of database under test * @param string $collectionName Name of collection under test */ - public function testAtlasDataLake(stdClass $test, array $runOn = null, array $data, $databaseName = null, $collectionName = null) + public function testAtlasDataLake(stdClass $test, ?array $runOn, array $data, ?string $databaseName = null, ?string $collectionName = null): void { if (isset($runOn)) { $this->checkServerRequirements($runOn); @@ -86,7 +83,7 @@ public function testAtlasDataLake(stdClass $test, array $runOn = null, array $da } if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromCrud((array) $test->expectations); + $commandExpectations = CommandExpectations::fromCrud($context->getClient(), (array) $test->expectations); $commandExpectations->startMonitoring(); } @@ -113,8 +110,10 @@ public function provideTests() $group = basename($filename, '.json'); $runOn = $json->runOn ?? null; $data = $json->data ?? []; + // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps $databaseName = $json->database_name ?? null; $collectionName = $json->collection_name ?? null; + // phpcs:enable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps foreach ($json->tests as $test) { $name = $group . ': ' . $test->description; @@ -128,17 +127,17 @@ public function provideTests() /** * Prose test 1: Connect without authentication */ - public function testKillCursors() + public function testKillCursors(): void { $cursorId = null; $cursorNamespace = null; (new CommandObserver())->observe( - function () { - $client = new Client(static::getUri()); + function (): void { + $client = static::createTestClient(); $client->test->driverdata->find([], ['batchSize' => 2, 'limit' => 3]); }, - function (array $event) use (&$cursorId, &$cursorNamespace) { + function (array $event) use (&$cursorId, &$cursorNamespace): void { if ($event['started']->getCommandName() === 'find') { $this->assertArrayHasKey('succeeded', $event); @@ -166,7 +165,7 @@ function (array $event) use (&$cursorId, &$cursorNamespace) { $this->assertIsInt($cursorId); $this->assertIsString($cursorNamespace); - list($databaseName, $collectionName) = explode('.', $cursorNamespace, 2); + [$databaseName, $collectionName] = explode('.', $cursorNamespace, 2); $command = $event['started']->getCommand(); /* Assert that the killCursors command uses the namespace and @@ -194,7 +193,7 @@ function (array $event) use (&$cursorId, &$cursorNamespace) { /** * Prose test 2: Connect without authentication */ - public function testConnectWithoutAuth() + public function testConnectWithoutAuth(): void { /* Parse URI to remove userinfo component. The query string is left * as-is and must not include authMechanism or credentials. */ @@ -205,7 +204,7 @@ public function testConnectWithoutAuth() $uri = $parts['scheme'] . '://' . $parts['host'] . $port . $path . $query; - $client = new Client($uri); + $client = static::createTestClient($uri); $cursor = $client->selectDatabase($this->getDatabaseName())->command(['ping' => 1]); $this->assertInstanceOf(Cursor::class, $cursor); @@ -215,9 +214,9 @@ public function testConnectWithoutAuth() /** * Prose test 3: Connect with SCRAM-SHA-1 authentication */ - public function testConnectwithSCRAMSHA1() + public function testConnectwithSCRAMSHA1(): void { - $client = new Client(static::getUri(), ['authMechanism' => 'SCRAM-SHA-1']); + $client = static::createTestClient(null, ['authMechanism' => 'SCRAM-SHA-1']); $cursor = $client->selectDatabase($this->getDatabaseName())->command(['ping' => 1]); $this->assertInstanceOf(Cursor::class, $cursor); @@ -227,16 +226,16 @@ public function testConnectwithSCRAMSHA1() /** * Prose test 4: Connect with SCRAM-SHA-256 authentication */ - public function testConnectwithSCRAMSHA256() + public function testConnectwithSCRAMSHA256(): void { - $client = new Client(static::getUri(), ['authMechanism' => 'SCRAM-SHA-256']); + $client = static::createTestClient(null, ['authMechanism' => 'SCRAM-SHA-256']); $cursor = $client->selectDatabase($this->getDatabaseName())->command(['ping' => 1]); $this->assertInstanceOf(Cursor::class, $cursor); $this->assertCommandSucceeded(current($cursor->toArray())); } - private function isAtlasDataLake() : bool + private function isAtlasDataLake(): bool { $cursor = $this->manager->executeCommand( $this->getDatabaseName(), diff --git a/tests/SpecTests/ChangeStreamsSpecTest.php b/tests/SpecTests/ChangeStreamsSpecTest.php deleted file mode 100644 index 5dcb39132..000000000 --- a/tests/SpecTests/ChangeStreamsSpecTest.php +++ /dev/null @@ -1,295 +0,0 @@ -getMore) && $expected->getMore === 42) { - static::assertObjectHasAttribute('getMore', $actual); - static::assertThat($actual->getMore, static::logicalOr( - static::isInstanceOf(Int64::class), - static::isType('integer') - )); - unset($expected->getMore); - } - - static::assertDocumentsMatch($expected, $actual); - } - - /** - * Assert that the expected and actual documents match. - * - * @param array $expectedDocuments Expected documents - * @param array $actualDocuments Actual documents - */ - public static function assertResult(array $expectedDocuments, array $actualDocuments) - { - static::assertCount(count($expectedDocuments), $actualDocuments); - - $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); - $mi->attachIterator(new ArrayIterator($expectedDocuments)); - $mi->attachIterator(new ArrayIterator($actualDocuments)); - - foreach ($mi as $documents) { - list($expectedDocument, $actualDocument) = $documents; - - $constraint = new DocumentsMatchConstraint($expectedDocument, true, true, ['42']); - - static::assertThat($actualDocument, $constraint); - } - } - - /** - * Execute an individual test case from the specification. - * - * @dataProvider provideTests - * @param stdClass $test Individual "tests[]" document - * @param string $databaseName Name of database under test - * @param string $collectionName Name of collection under test - * @param string $database2Name Name of alternate database under test - * @param string $collection2Name Name of alternate collection under test - */ - public function testChangeStreams(stdClass $test, $databaseName = null, $collectionName = null, $database2Name = null, $collection2Name = null) - { - if (isset(self::$incompleteTests[$this->dataDescription()])) { - $this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]); - } - - if ($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets()) { - $this->markTestSkipped('$changeStream is only supported with replicasets'); - } - - $this->checkServerRequirements($this->createRunOn($test)); - - if (! isset($databaseName, $collectionName)) { - $this->fail('Required database and collection names are unset'); - } - - $context = Context::fromChangeStreams($test, $databaseName, $collectionName); - $this->setContext($context); - - $this->dropDatabasesAndCreateCollection($databaseName, $collectionName); - - if (isset($database2Name, $collection2Name)) { - $this->dropDatabasesAndCreateCollection($database2Name, $collection2Name); - } - - if (isset($test->failPoint)) { - $this->configureFailPoint($test->failPoint); - } - - if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromChangeStreams($test->expectations); - $commandExpectations->startMonitoring(); - } - - $errorExpectation = ErrorExpectation::fromChangeStreams($test->result); - $resultExpectation = ResultExpectation::fromChangeStreams($test->result, [$this, 'assertResult']); - - $result = null; - $exception = null; - - try { - $changeStream = $this->createChangeStream($test); - } catch (Exception $e) { - $exception = $e; - } - - if (isset($commandExpectations)) { - $commandExpectations->stopMonitoring(); - } - - foreach ($test->operations as $operation) { - Operation::fromChangeStreams($operation)->assert($this, $context); - } - - if (isset($commandExpectations)) { - $commandExpectations->startMonitoring(); - } - - /* If the change stream was successfully created (i.e. $exception is - * null), attempt to iterate up to the expected number of results. It's - * possible that some errors (e.g. projecting out _id) will only be - * thrown during iteration, so we must also try/catch here. */ - try { - if (isset($changeStream)) { - $limit = isset($test->result->success) ? count($test->result->success) : 0; - $result = $this->iterateChangeStream($changeStream, $limit); - } - } catch (Exception $e) { - $this->assertNull($exception); - $exception = $e; - } - - $errorExpectation->assert($this, $exception); - $resultExpectation->assert($this, $result); - - if (isset($commandExpectations)) { - $commandExpectations->stopMonitoring(); - $commandExpectations->assert($this, $context); - } - } - - public function provideTests() - { - $testArgs = []; - - foreach (glob(__DIR__ . '/change-streams/*.json') as $filename) { - $json = $this->decodeJson(file_get_contents($filename)); - $group = basename($filename, '.json'); - $databaseName = $json->database_name ?? null; - $database2Name = $json->database2_name ?? null; - $collectionName = $json->collection_name ?? null; - $collection2Name = $json->collection2_name ?? null; - - foreach ($json->tests as $test) { - $name = $group . ': ' . $test->description; - $testArgs[$name] = [$test, $databaseName, $collectionName, $database2Name, $collection2Name]; - } - } - - return $testArgs; - } - - /** - * Create a change stream. - * - * @param stdClass $test - * @return ChangeStream - * @throws LogicException if the target is unsupported - */ - private function createChangeStream(stdClass $test) - { - $context = $this->getContext(); - $pipeline = $test->changeStreamPipeline ?? []; - $options = isset($test->changeStreamOptions) ? (array) $test->changeStreamOptions : []; - - switch ($test->target) { - case 'client': - return $context->getClient()->watch($pipeline, $options); - case 'database': - return $context->getDatabase()->watch($pipeline, $options); - case 'collection': - return $context->getCollection()->watch($pipeline, $options); - default: - throw new LogicException('Unsupported target: ' . $test->target); - } - } - - /** - * Convert the server requirements to a standard "runOn" array used by other - * specifications. - * - * @param stdClass $test - * @return array - */ - private function createRunOn(stdClass $test) - { - $req = new stdClass(); - - /* Append ".99" as patch version, since command monitoring tests expect - * the minor version to be an inclusive upper bound. */ - if (isset($test->maxServerVersion)) { - $req->maxServerVersion = $test->maxServerVersion; - } - - if (isset($test->minServerVersion)) { - $req->minServerVersion = $test->minServerVersion; - } - - if (isset($test->topology)) { - $req->topology = $test->topology; - } - - return [$req]; - } - - /** - * Drop the database and create the collection. - * - * @param string $databaseName - * @param string $collectionName - */ - private function dropDatabasesAndCreateCollection($databaseName, $collectionName) - { - $context = $this->getContext(); - - $database = $context->getClient()->selectDatabase($databaseName); - $database->drop($context->defaultWriteOptions); - $database->createCollection($collectionName, $context->defaultWriteOptions); - } - - /** - * Iterate a change stream. - * - * @param ChangeStream $changeStream - * @param integer $limit - * @return BSONDocument[] - */ - private function iterateChangeStream(ChangeStream $changeStream, $limit = 0) - { - if ($limit < 0) { - throw new LogicException('$limit is negative'); - } - - /* Limit iterations to guard against an infinite loop should a test fail - * to return as many results as are expected. Require at least one - * iteration to allow next() a chance to throw for error tests. */ - $maxIterations = $limit + 1; - - /* On sharded clusters, allow for empty getMore calls due to sharding - * architecture */ - if ($this->isShardedCluster()) { - $maxIterations *= 5; - } - - $events = []; - - for ($i = 0, $changeStream->rewind(); $i < $maxIterations; $i++, $changeStream->next()) { - if (! $changeStream->valid()) { - continue; - } - - $event = $changeStream->current(); - $this->assertInstanceOf(BSONDocument::class, $event); - $events[] = $event; - - if (count($events) >= $limit) { - break; - } - } - - return $events; - } -} diff --git a/tests/SpecTests/ClientSideEncryption/FunctionalTestCase.php b/tests/SpecTests/ClientSideEncryption/FunctionalTestCase.php new file mode 100644 index 000000000..a48ea03ad --- /dev/null +++ b/tests/SpecTests/ClientSideEncryption/FunctionalTestCase.php @@ -0,0 +1,119 @@ +skipIfClientSideEncryptionIsNotSupported(); + + if (! static::isCryptSharedLibAvailable() && ! static::isMongocryptdAvailable()) { + $this->markTestSkipped('Neither crypt_shared nor mongocryptd are available'); + } + } + + public static function createTestClient(?string $uri = null, array $options = [], array $driverOptions = []): Client + { + if (isset($driverOptions['autoEncryption']) && getenv('CRYPT_SHARED_LIB_PATH')) { + $driverOptions['autoEncryption']['extraOptions']['cryptSharedLibPath'] = getenv('CRYPT_SHARED_LIB_PATH'); + } + + return parent::createTestClient($uri, $options, $driverOptions); + } + + protected static function getAWSCredentials(): array + { + return [ + 'accessKeyId' => static::getEnv('AWS_ACCESS_KEY_ID'), + 'secretAccessKey' => static::getEnv('AWS_SECRET_ACCESS_KEY'), + ]; + } + + protected static function insertKeyVaultData(Client $client, ?array $keyVaultData = null): void + { + $collection = $client->selectCollection('keyvault', 'datakeys', ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]); + $collection->drop(); + + if (empty($keyVaultData)) { + return; + } + + $collection->insertMany($keyVaultData); + } + + private function createTestCollection(?stdClass $encryptedFields = null, ?stdClass $jsonSchema = null): void + { + $context = $this->getContext(); + $options = $context->defaultWriteOptions; + + if (! empty($encryptedFields)) { + $options['encryptedFields'] = $encryptedFields; + } + + if (! empty($jsonSchema)) { + $options['validator'] = ['$jsonSchema' => $jsonSchema]; + } + + $context->getDatabase()->createCollection($context->collectionName, $options); + } + + private static function getEnv(string $name): string + { + $value = getenv($name); + + if ($value === false) { + Assert::markTestSkipped(sprintf('Environment variable "%s" is not defined', $name)); + } + + return $value; + } + + private static function isCryptSharedLibAvailable(): bool + { + $cryptSharedLibPath = getenv('CRYPT_SHARED_LIB_PATH'); + + if ($cryptSharedLibPath === false) { + return false; + } + + return is_readable($cryptSharedLibPath); + } + + private static function isMongocryptdAvailable(): bool + { + $paths = explode(PATH_SEPARATOR, getenv("PATH")); + + foreach ($paths as $path) { + if (is_executable($path . DIRECTORY_SEPARATOR . 'mongocryptd')) { + return true; + } + } + + return false; + } +} diff --git a/tests/SpecTests/ClientSideEncryption/Prose21_AutomaticDataEncryptionKeysTest.php b/tests/SpecTests/ClientSideEncryption/Prose21_AutomaticDataEncryptionKeysTest.php new file mode 100644 index 000000000..ea99ca1c2 --- /dev/null +++ b/tests/SpecTests/ClientSideEncryption/Prose21_AutomaticDataEncryptionKeysTest.php @@ -0,0 +1,168 @@ +isStandalone() || ($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets())) { + $this->markTestSkipped('Automatic data encryption key tests require replica sets'); + } + + $this->skipIfServerVersion('<', '7.0.0', 'Automatic data encryption key tests require MongoDB 7.0 or later'); + + $client = static::createTestClient(); + $this->database = $client->selectDatabase($this->getDatabaseName()); + + /* Since test cases may create encrypted collections, ensure that the + * collection and any "enxcol_" collections do not exist. Specify + * "encryptedFields" to ensure "enxcol_" collections are handled. */ + $this->database->dropCollection($this->getCollectionName(), ['encryptedFields' => []]); + + // Ensure that the key vault is dropped with a majority write concern + self::insertKeyVaultData($client, []); + + $this->clientEncryption = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'aws' => self::getAWSCredentials(), + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + ], + ]); + } + + public function tearDown(): void + { + $this->clientEncryption = null; + $this->database = null; + } + + /** + * @see https://github.com/mongodb/specifications/blob/bc37892f360cab9df4082922384e0f4d4233f6d3/source/client-side-encryption/tests/README.rst#case-1-simple-creation-and-validation + * @dataProvider provideKmsProviderAndMasterKey + */ + public function testCase1_SimpleCreationAndValidation(string $kmsProvider, ?array $masterKey): void + { + [$result, $encryptedFields] = $this->database->createEncryptedCollection( + $this->getCollectionName(), + $this->clientEncryption, + $kmsProvider, + $masterKey, + ['encryptedFields' => ['fields' => [['path' => 'ssn', 'bsonType' => 'string', 'keyId' => null]]]] + ); + + $this->assertCommandSucceeded($result); + $this->assertInstanceOf(Binary::class, $encryptedFields['fields'][0]['keyId'] ?? null); + + $this->expectException(BulkWriteException::class); + $this->expectExceptionMessage('Document failed validation'); + $this->database->selectCollection($this->getCollectionName())->insertOne(['ssn' => '123-45-6789']); + } + + public static function provideKmsProviderAndMasterKey(): Generator + { + yield [ + 'aws', + ['region' => 'us-east-1', 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0'], + ]; + + yield [ + 'local', + null, + ]; + } + + /** + * @see https://github.com/mongodb/specifications/blob/bc37892f360cab9df4082922384e0f4d4233f6d3/source/client-side-encryption/tests/README.rst#case-2-missing-encryptedfields + * @dataProvider provideKmsProviderAndMasterKey + */ + public function testCase2_MissingEncryptedFields(string $kmsProvider, ?array $masterKey): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('"encryptedFields" option'); + $this->database->createEncryptedCollection( + $this->getCollectionName(), + $this->clientEncryption, + $kmsProvider, + $masterKey, + [] + ); + } + + /** + * @see https://github.com/mongodb/specifications/blob/bc37892f360cab9df4082922384e0f4d4233f6d3/source/client-side-encryption/tests/README.rst#case-3-invalid-keyid + * @dataProvider provideKmsProviderAndMasterKey + */ + public function testCase3_InvalidKeyId(string $kmsProvider, ?array $masterKey): void + { + try { + $this->database->createEncryptedCollection( + $this->getCollectionName(), + $this->clientEncryption, + $kmsProvider, + $masterKey, + ['encryptedFields' => ['fields' => [['path' => 'ssn', 'bsonType' => 'string', 'keyId' => false]]]] + ); + $this->fail('CreateEncryptedCollectionException was not thrown'); + } catch (CreateEncryptedCollectionException $e) { + $this->assertFalse($e->getEncryptedFields()['fields'][0]['keyId'], 'Invalid keyId should not be modified'); + + $previous = $e->getPrevious(); + $this->assertInstanceOf(CommandException::class, $previous); + $this->assertSame(self::SERVER_ERROR_TYPEMISMATCH, $previous->getCode()); + $this->assertStringContainsString('keyId', $previous->getMessage()); + } + } + + /** + * @see https://github.com/mongodb/specifications/blob/bc37892f360cab9df4082922384e0f4d4233f6d3/source/client-side-encryption/tests/README.rst#case-4-insert-encrypted-value + * @dataProvider provideKmsProviderAndMasterKey + */ + public function testCase4_InsertEncryptedValue(string $kmsProvider, ?array $masterKey): void + { + [$result, $encryptedFields] = $this->database->createEncryptedCollection( + $this->getCollectionName(), + $this->clientEncryption, + $kmsProvider, + $masterKey, + ['encryptedFields' => ['fields' => [['path' => 'ssn', 'bsonType' => 'string', 'keyId' => null]]]] + ); + + $this->assertCommandSucceeded($result); + $this->assertInstanceOf(Binary::class, $encryptedFields['fields'][0]['keyId'] ?? null); + + $encrypted = $this->clientEncryption->encrypt('123-45-6789', [ + 'keyId' => $encryptedFields['fields'][0]['keyId'], + 'algorithm' => ClientEncryption::ALGORITHM_UNINDEXED, + ]); + + $collection = $this->database->selectCollection($this->getCollectionName()); + $insertedId = $collection->insertOne(['ssn' => $encrypted])->getInsertedId(); + $this->assertNotNull($collection->findOne(['_id' => $insertedId])); + } +} diff --git a/tests/SpecTests/ClientSideEncryption/Prose22_RangeExplicitEncryptionTest.php b/tests/SpecTests/ClientSideEncryption/Prose22_RangeExplicitEncryptionTest.php new file mode 100644 index 000000000..46de1c49f --- /dev/null +++ b/tests/SpecTests/ClientSideEncryption/Prose22_RangeExplicitEncryptionTest.php @@ -0,0 +1,478 @@ +isStandalone() || ($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets())) { + $this->markTestSkipped('Range explicit encryption tests require replica sets'); + } + + $this->skipIfServerVersion('<', '7.0.0', 'Range explicit encryption tests require MongoDB 7.0 or later'); + + $client = static::createTestClient(); + + $key1Document = $this->decodeJson(file_get_contents(__DIR__ . '/../client-side-encryption/etc/data/keys/key1-document.json')); + $this->key1Id = $key1Document->_id; + + // Drop the key vault collection and insert key1Document with a majority write concern + self::insertKeyVaultData($client, [$key1Document]); + + $this->clientEncryption = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + ]); + + $autoEncryptionOpts = [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + 'bypassQueryAnalysis' => true, + ]; + + $this->encryptedClient = self::createTestClient(null, [], [ + 'autoEncryption' => $autoEncryptionOpts, + /* libmongocrypt caches results from listCollections. Use a new + * client in each test to ensure its encryptedFields is applied. */ + 'disableClientPersistence' => true, + ]); + } + + public function setUpWithTypeAndRangeOpts(string $type, array $rangeOpts): void + { + if ($type === 'DecimalNoPrecision' || $type === 'DecimalPrecision') { + $this->markTestSkipped('Bundled libmongocrypt does not support Decimal128 (PHPC-2207)'); + } + + /* Read the encryptedFields file directly into BSON to preserve typing + * for 64-bit integers. This means that DropEncryptedCollection and + * CreateEncryptedCollection will be unable to inspect the option for + * metadata collection names, but that's not necessary for the test. */ + $encryptedFields = Document::fromJSON(file_get_contents(__DIR__ . '/../client-side-encryption/etc/data/range-encryptedFields-' . $type . '.json')); + + $database = $this->encryptedClient->selectDatabase($this->getDatabaseName()); + $database->dropCollection('explicit_encryption', ['encryptedFields' => $encryptedFields]); + $database->createCollection('explicit_encryption', ['encryptedFields' => $encryptedFields]); + $this->collection = $database->selectCollection('explicit_encryption'); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE_PREVIEW, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $cast = self::getCastCallableForType($type); + $fieldName = 'encrypted' . $type; + + $this->collection->insertMany([ + ['_id' => 0, $fieldName => $this->clientEncryption->encrypt($cast(0), $encryptOpts)], + ['_id' => 1, $fieldName => $this->clientEncryption->encrypt($cast(6), $encryptOpts)], + ['_id' => 2, $fieldName => $this->clientEncryption->encrypt($cast(30), $encryptOpts)], + ['_id' => 3, $fieldName => $this->clientEncryption->encrypt($cast(200), $encryptOpts)], + ]); + } + + public function tearDown(): void + { + /* Since encryptedClient is created with disableClientPersistence=true, + * free any objects that may hold a reference to its mongoc_client_t */ + $this->collection = null; + $this->clientEncryption = null; + $this->encryptedClient = null; + } + + /** @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#test-setup-rangeopts */ + public static function provideTypeAndRangeOpts(): Generator + { + // TODO: skip DecimalNoPrecision test on mongos + yield 'DecimalNoPrecision' => [ + 'DecimalNoPrecision', + ['sparsity' => 1], + ]; + + yield 'DecimalPrecision' => [ + 'DecimalPrecision', + [ + 'min' => new Decimal128('0'), + 'max' => new Decimal128('200'), + 'sparsity' => 1, + 'precision' => 2, + ], + ]; + + yield 'DoubleNoPrecision' => [ + 'DoubleNoPrecision', + ['sparsity' => 1], + ]; + + yield 'DoublePrecision' => [ + 'DoublePrecision', + [ + 'min' => 0.0, + 'max' => 200.0, + 'sparsity' => 1, + 'precision' => 2, + ], + ]; + + yield 'Date' => [ + 'Date', + [ + 'min' => new UTCDateTime(0), + 'max' => new UTCDateTime(200), + 'sparsity' => 1, + ], + ]; + + yield 'Int' => [ + 'Int', + [ + 'min' => 0, + 'max' => 200, + 'sparsity' => 1, + ], + ]; + + yield 'Long' => [ + 'Long', + [ + 'min' => new Int64(0), + 'max' => new Int64(200), + 'sparsity' => 1, + ], + ]; + } + + /** + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-1-can-decrypt-a-payload + * @dataProvider provideTypeAndRangeOpts + */ + public function testCase1_CanDecryptAPayload(string $type, array $rangeOpts): void + { + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE_PREVIEW, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $cast = self::getCastCallableForType($type); + $originalValue = $cast(6); + + $insertPayload = $this->clientEncryption->encrypt($originalValue, $encryptOpts); + $decryptedValue = $this->clientEncryption->decrypt($insertPayload); + + /* Decryption of a 64-bit integer will likely result in a scalar int, so + * cast it back to an Int64 before comparing to the original value. */ + if ($type === 'Long' && is_int($decryptedValue)) { + $decryptedValue = $cast($decryptedValue); + } + + /* Use separate assertions for type and equality as assertSame isn't + * suitable for comparing BSON objects and using assertEquals alone + * would disregard scalar type differences. */ + $this->assertSame(get_debug_type($originalValue), get_debug_type($decryptedValue)); + $this->assertEquals($originalValue, $decryptedValue); + } + + /** + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-2-can-find-encrypted-range-and-return-the-maximum + * @dataProvider provideTypeAndRangeOpts + */ + public function testCase2_CanFindEncryptedRangeAndReturnTheMaximum(string $type, array $rangeOpts): void + { + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE_PREVIEW, + 'queryType' => ClientEncryption::QUERY_TYPE_RANGE_PREVIEW, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $cast = self::getCastCallableForType($type); + $fieldName = 'encrypted' . $type; + + $expr = [ + '$and' => [ + [$fieldName => ['$gte' => $cast(6)]], + [$fieldName => ['$lte' => $cast(200)]], + ], + ]; + + $encryptedExpr = $this->clientEncryption->encryptExpression($expr, $encryptOpts); + $cursor = $this->collection->find($encryptedExpr, ['sort' => ['_id' => 1]]); + + $expectedDocuments = [ + ['_id' => 1, $fieldName => $cast(6)], + ['_id' => 2, $fieldName => $cast(30)], + ['_id' => 3, $fieldName => $cast(200)], + ]; + + $this->assertMultipleDocumentsMatch($expectedDocuments, $cursor); + } + + /** + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-3-can-find-encrypted-range-and-return-the-minimum + * @dataProvider provideTypeAndRangeOpts + */ + public function testCase3_CanFindEncryptedRangeAndReturnTheMinimum(string $type, array $rangeOpts): void + { + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE_PREVIEW, + 'queryType' => ClientEncryption::QUERY_TYPE_RANGE_PREVIEW, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $cast = self::getCastCallableForType($type); + $fieldName = 'encrypted' . $type; + + $expr = [ + '$and' => [ + [$fieldName => ['$gte' => $cast(0)]], + [$fieldName => ['$lte' => $cast(6)]], + ], + ]; + + $encryptedExpr = $this->clientEncryption->encryptExpression($expr, $encryptOpts); + $cursor = $this->collection->find($encryptedExpr, ['sort' => ['_id' => 1]]); + + $expectedDocuments = [ + ['_id' => 0, $fieldName => $cast(0)], + ['_id' => 1, $fieldName => $cast(6)], + ]; + + $this->assertMultipleDocumentsMatch($expectedDocuments, $cursor); + } + + /** + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-4-can-find-encrypted-range-with-an-open-range-query + * @dataProvider provideTypeAndRangeOpts + */ + public function testCase4_CanFindEncryptedRangeWithAnOpenRangeQuery(string $type, array $rangeOpts): void + { + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE_PREVIEW, + 'queryType' => ClientEncryption::QUERY_TYPE_RANGE_PREVIEW, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $cast = self::getCastCallableForType($type); + $fieldName = 'encrypted' . $type; + + $expr = ['$and' => [[$fieldName => ['$gt' => $cast(30)]]]]; + + $encryptedExpr = $this->clientEncryption->encryptExpression($expr, $encryptOpts); + $cursor = $this->collection->find($encryptedExpr, ['sort' => ['_id' => 1]]); + $expectedDocuments = [['_id' => 3, $fieldName => $cast(200)]]; + + $this->assertMultipleDocumentsMatch($expectedDocuments, $cursor); + } + + /** + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-5-can-run-an-aggregation-expression-inside-expr + * @dataProvider provideTypeAndRangeOpts + */ + public function testCase5_CanRunAnAggregationExpressionInsideExpr(string $type, array $rangeOpts): void + { + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE_PREVIEW, + 'queryType' => ClientEncryption::QUERY_TYPE_RANGE_PREVIEW, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $cast = self::getCastCallableForType($type); + $fieldName = 'encrypted' . $type; + $fieldPath = '$' . $fieldName; + + $expr = ['$and' => [['$lt' => [$fieldPath, $cast(30)]]]]; + + $encryptedExpr = $this->clientEncryption->encryptExpression($expr, $encryptOpts); + $cursor = $this->collection->find(['$expr' => $encryptedExpr], ['sort' => ['_id' => 1]]); + + $expectedDocuments = [ + ['_id' => 0, $fieldName => $cast(0)], + ['_id' => 1, $fieldName => $cast(6)], + ]; + + $this->assertMultipleDocumentsMatch($expectedDocuments, $cursor); + } + + /** + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-6-encrypting-a-document-greater-than-the-maximum-errors + * @dataProvider provideTypeAndRangeOpts + */ + public function testCase6_EncryptingADocumentGreaterThanTheMaximumErrors(string $type, array $rangeOpts): void + { + if ($type === 'DecimalNoPrecision' || $type === 'DoubleNoPrecision') { + $this->markTestSkipped('Test is not applicable to "NoPrecision" types'); + } + + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE_PREVIEW, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $cast = self::getCastCallableForType($type); + + $this->expectException(EncryptionException::class); + $this->expectExceptionMessage('Value must be greater than or equal to the minimum value and less than or equal to the maximum value'); + $this->clientEncryption->encrypt($cast(201), $encryptOpts); + } + + /** + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-7-encrypting-a-value-of-a-different-type-errors + * @dataProvider provideTypeAndRangeOpts + */ + public function testCase7_EncryptingAValueOfADifferentTypeErrors(string $type, array $rangeOpts): void + { + if ($type === 'DecimalNoPrecision' || $type === 'DoubleNoPrecision') { + /* Explicit encryption relies on min/max range options to check + * types and "NoPrecision" intentionally omits those options. */ + $this->markTestSkipped('Test is not applicable to DoubleNoPrecision and DecimalNoPrecision'); + } + + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE_PREVIEW, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $value = $type === 'Int' ? 6.0 : 6; + + $this->expectException(EncryptionException::class); + $this->expectExceptionMessage('expected matching \'min\' and value type'); + $this->clientEncryption->encrypt($value, $encryptOpts); + } + + /** + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-8-setting-precision-errors-if-the-type-is-not-double-or-decimal128 + * @dataProvider provideTypeAndRangeOpts + */ + public function testCase8_SettingPrecisionErrorsIfTheTypeIsNotDoubleOrDecimal128(string $type, array $rangeOpts): void + { + if ($type === 'DecimalNoPrecision' || $type === 'DecimalPrecision' || $type === 'DoubleNoPrecision' || $type === 'DoublePrecision') { + $this->markTestSkipped('Test is not applicable to Double and Decimal types'); + } + + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE_PREVIEW, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts + ['precision' => 2], + ]; + + $cast = self::getCastCallableForType($type); + + $this->expectException(EncryptionException::class); + $this->expectExceptionMessage('expected \'precision\' to be set with double or decimal128 index'); + $this->clientEncryption->encrypt($cast(6), $encryptOpts); + } + + private function assertMultipleDocumentsMatch(array $expectedDocuments, Iterator $actualDocuments): void + { + $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); + $mi->attachIterator(new ArrayIterator($expectedDocuments)); + $mi->attachIterator($actualDocuments); + + foreach ($mi as $documents) { + [$expectedDocument, $actualDocument] = $documents; + $this->assertNotNull($expectedDocument); + $this->assertNotNull($actualDocument); + + $this->assertDocumentsMatch($expectedDocument, $actualDocument); + } + } + + private static function getCastCallableForType(string $type): callable + { + switch ($type) { + case 'DecimalNoPrecision': + case 'DecimalPrecision': + return function (int $value) { + return new Decimal128((string) $value); + }; + + case 'DoubleNoPrecision': + case 'DoublePrecision': + return function (int $value) { + return (double) $value; + }; + + case 'Date': + return function (int $value) { + return new UTCDateTime($value); + }; + + case 'Int': + return function (int $value) { + return $value; + }; + + case 'Long': + return function (int $value) { + return new Int64($value); + }; + + default: + throw new LogicException('Unsupported type: ' . $type); + } + } +} diff --git a/tests/SpecTests/ClientSideEncryptionSpecTest.php b/tests/SpecTests/ClientSideEncryptionSpecTest.php index f2edd7f6b..6d30a526e 100644 --- a/tests/SpecTests/ClientSideEncryptionSpecTest.php +++ b/tests/SpecTests/ClientSideEncryptionSpecTest.php @@ -4,64 +4,155 @@ use Closure; use MongoDB\BSON\Binary; +use MongoDB\BSON\Document; use MongoDB\BSON\Int64; use MongoDB\Client; use MongoDB\Collection; use MongoDB\Driver\ClientEncryption; use MongoDB\Driver\Exception\AuthenticationException; use MongoDB\Driver\Exception\BulkWriteException; +use MongoDB\Driver\Exception\CommandException; use MongoDB\Driver\Exception\ConnectionException; use MongoDB\Driver\Exception\ConnectionTimeoutException; use MongoDB\Driver\Exception\EncryptionException; use MongoDB\Driver\Exception\RuntimeException; +use MongoDB\Driver\Exception\ServerException; +use MongoDB\Driver\Monitoring\CommandFailedEvent; +use MongoDB\Driver\Monitoring\CommandStartedEvent; +use MongoDB\Driver\Monitoring\CommandSubscriber; +use MongoDB\Driver\Monitoring\CommandSucceededEvent; use MongoDB\Driver\WriteConcern; -use MongoDB\Operation\CreateCollection; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Assert; use PHPUnit\Framework\SkippedTestError; use stdClass; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; use Throwable; use UnexpectedValueException; + use function base64_decode; use function basename; +use function count; +use function explode; use function file_get_contents; +use function getenv; use function glob; +use function in_array; +use function is_executable; +use function is_readable; use function iterator_to_array; use function json_decode; use function sprintf; use function str_repeat; -use function strlen; -use function unserialize; +use function substr; + +use const DIRECTORY_SEPARATOR; +use const PATH_SEPARATOR; /** * Client-side encryption spec tests. * * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption + * @group csfle + * @group serverless */ class ClientSideEncryptionSpecTest extends FunctionalTestCase { - use SetUpTearDownTrait; - - const LOCAL_MASTERKEY = 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk'; - - private function doSetUp() + public const LOCAL_MASTERKEY = 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk'; + + /** @var array */ + private static $incompleteTests = [ + 'explain: Explain a find with deterministic encryption' => 'crypt_shared does not add apiVersion field to explain commands (PHPLIB-947, SERVER-69564)', + 'fle2v2-Range-Decimal-Aggregate: FLE2 Range Decimal. Aggregate.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Find with $gt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Find with $gte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Find with $gt with no results' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Find with $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Find with $lte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Find with $gt and $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Find with equality' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Find with $in' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Aggregate with $gte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Aggregate with $gt with no results' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Aggregate with $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Aggregate with $lte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Aggregate with $gt and $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Aggregate with equality' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Aggregate with $in' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Wrong type: Insert Int' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Correctness: Wrong type: Find Int' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Delete: FLE2 Range Decimal. Delete.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-FindOneAndUpdate: FLE2 Range Decimal. FindOneAndUpdate.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-InsertFind: FLE2 Range Decimal. Insert and Find.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Aggregate: FLE2 Range DecimalPrecision. Aggregate.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Find with $gt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Find with $gte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Find with $gt with no results' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Find with $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Find with $lte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Find with $lt below min' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Find with $gt above max' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Find with $gt and $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Find with equality' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Find with full range' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Find with $in' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Insert out of range' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Insert min and max' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Aggregate with $gte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Aggregate with $gt with no results' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Aggregate with $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Aggregate with $lte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Aggregate with $lt below min' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Aggregate with $gt above max' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Aggregate with $gt and $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Aggregate with equality' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Aggregate with full range' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Aggregate with $in' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Wrong type: Insert Int' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Correctness: Wrong type: Find Int' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Delete: FLE2 Range DecimalPrecision. Delete.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-FindOneAndUpdate: FLE2 Range DecimalPrecision. FindOneAndUpdate.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-InsertFind: FLE2 Range DecimalPrecision. Insert and Find.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-DecimalPrecision-Update: FLE2 Range DecimalPrecision. Update.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Range-Decimal-Update: FLE2 Range Decimal. Update.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'timeoutMS: timeoutMS applied to listCollections to get collection schema' => 'Not yet implemented (PHPC-1760)', + 'timeoutMS: remaining timeoutMS applied to find to get keyvault data' => 'Not yet implemented (PHPC-1760)', + ]; + + public function setUp(): void { parent::setUp(); $this->skipIfClientSideEncryptionIsNotSupported(); + + if (! static::isCryptSharedLibAvailable() && ! static::isMongocryptdAvailable()) { + $this->markTestSkipped('Neither crypt_shared nor mongocryptd are available'); + } } /** * Assert that the expected and actual command documents match. * + * Note: this method may modify the $expected object. + * * @param stdClass $expected Expected command document * @param stdClass $actual Actual command document */ - public static function assertCommandMatches(stdClass $expected, stdClass $actual) + public static function assertCommandMatches(stdClass $expected, stdClass $actual): void { + static::assertCommandOmittedFields($expected, $actual); + static::assertDocumentsMatch($expected, $actual); } + public static function createTestClient(?string $uri = null, array $options = [], array $driverOptions = []): Client + { + if (isset($driverOptions['autoEncryption']) && getenv('CRYPT_SHARED_LIB_PATH')) { + $driverOptions['autoEncryption']['extraOptions']['cryptSharedLibPath'] = getenv('CRYPT_SHARED_LIB_PATH'); + } + + return parent::createTestClient($uri, $options, $driverOptions); + } + /** * Execute an individual test case from the specification. * @@ -74,8 +165,12 @@ public static function assertCommandMatches(stdClass $expected, stdClass $actual * @param string $databaseName Name of database under test * @param string $collectionName Name of collection under test */ - public function testClientSideEncryption(stdClass $test, array $runOn = null, array $data, array $keyVaultData = null, $jsonSchema = null, $databaseName = null, $collectionName = null) + public function testClientSideEncryption(stdClass $test, ?array $runOn, array $data, ?stdClass $encryptedFields = null, ?array $keyVaultData = null, ?stdClass $jsonSchema = null, ?string $databaseName = null, ?string $collectionName = null): void { + if (isset(self::$incompleteTests[$this->dataDescription()])) { + $this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]); + } + if (isset($runOn)) { $this->checkServerRequirements($runOn); } @@ -95,19 +190,19 @@ public function testClientSideEncryption(stdClass $test, array $runOn = null, ar $this->setContext($context); - $this->insertKeyVaultData($keyVaultData); - $this->dropTestAndOutcomeCollections(); - $this->createTestCollection($jsonSchema); + self::insertKeyVaultData($context->getClient(), $keyVaultData); + $this->dropTestAndOutcomeCollections(empty($encryptedFields) ? [] : ['encryptedFields' => $encryptedFields]); + $this->createTestCollection($encryptedFields, $jsonSchema); $this->insertDataFixtures($data); if (isset($test->failPoint)) { $this->configureFailPoint($test->failPoint); } - $context->enableEncryption(); + $context->useEncryptedClientIfConfigured = true; if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromClientSideEncryption($test->expectations); + $commandExpectations = CommandExpectations::fromClientSideEncryption($context->getClient(), $test->expectations); $commandExpectations->startMonitoring(); } @@ -120,7 +215,7 @@ public function testClientSideEncryption(stdClass $test, array $runOn = null, ar $commandExpectations->assert($this, $context); } - $context->disableEncryption(); + $context->useEncryptedClientIfConfigured = false; if (isset($test->outcome->collection->data)) { $this->assertOutcomeCollectionData($test->outcome->collection->data, ResultExpectation::ASSERT_DOCUMENTS_MATCH); @@ -134,8 +229,21 @@ public function provideTests() foreach (glob(__DIR__ . '/client-side-encryption/tests/*.json') as $filename) { $group = basename($filename, '.json'); + /* Some tests need to differentiate int32 and int64 BSON types. + * Decode certain field paths as BSON to preserve type information + * that would otherwise be lost by round-tripping through PHP. */ + $typeMap = [ + 'fieldPaths' => [ + 'encrypted_fields.fields' => 'bson', + 'tests.$.operations.$.arguments.document' => 'bson', + 'tests.$.operations.$.arguments.pipeline.$' => 'bson', + 'tests.$.operations.$.arguments.filter' => 'bson', + 'tests.$.operations.$.arguments.update' => 'bson', + ], + ]; + try { - $json = $this->decodeJson(file_get_contents($filename)); + $json = Document::fromJSON(file_get_contents($filename))->toPHP($typeMap); } catch (Throwable $e) { $testArgs[$group] = [ (object) ['skipReason' => sprintf('Exception loading file "%s": %s', $filename, $e->getMessage())], @@ -148,14 +256,17 @@ public function provideTests() $runOn = $json->runOn ?? null; $data = $json->data ?? []; + // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + $encryptedFields = $json->encrypted_fields ?? null; $keyVaultData = $json->key_vault_data ?? null; $jsonSchema = $json->json_schema ?? null; $databaseName = $json->database_name ?? null; $collectionName = $json->collection_name ?? null; + // phpcs:enable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps foreach ($json->tests as $test) { $name = $group . ': ' . $test->description; - $testArgs[$name] = [$test, $runOn, $data, $keyVaultData, $jsonSchema, $databaseName, $collectionName]; + $testArgs[$name] = [$test, $runOn, $data, $encryptedFields, $keyVaultData, $jsonSchema, $databaseName, $collectionName]; } } @@ -163,22 +274,30 @@ public function provideTests() } /** - * Prose test: Data key and double encryption + * Prose test 2: Data Key and Double Encryption * + * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#data-key-and-double-encryption * @dataProvider dataKeyProvider */ - public function testDataKeyAndDoubleEncryption(Closure $test) + public function testDataKeyAndDoubleEncryption(string $providerName, $masterKey): void { - $client = new Client(static::getUri()); - - $client->selectCollection('keyvault', 'datakeys')->drop(); + $client = static::createTestClient(); $client->selectCollection('db', 'coll')->drop(); + // Ensure that the key vault is dropped with a majority write concern + self::insertKeyVaultData($client, []); + $encryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', 'kmsProviders' => [ 'aws' => Context::getAWSCredentials(), + 'azure' => Context::getAzureCredentials(), + 'gcp' => Context::getGCPCredentials(), 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + 'kmip' => ['endpoint' => Context::getKmipEndpoint()], + ], + 'tlsOptions' => [ + 'kmip' => Context::getKmsTlsOptions(), ], ]; @@ -200,131 +319,114 @@ public function testDataKeyAndDoubleEncryption(Closure $test) 'keyVaultClient' => $client, ]; - $clientEncrypted = new Client(static::getUri(), [], ['autoEncryption' => $autoEncryptionOpts]); + $clientEncrypted = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); $clientEncryption = $clientEncrypted->createClientEncryption($encryptionOpts); - $test($clientEncryption, $client, $clientEncrypted, $this); - } + $dataKeyId = null; + $insertCommand = null; - public static function dataKeyProvider() - { - return [ - 'local' => [ - static function (ClientEncryption $clientEncryption, Client $client, Client $clientEncrypted, self $test) { - $commands = []; + $keyAltName = $providerName . '_altname'; - $localDatakeyId = null; + (new CommandObserver())->observe( + function () use ($clientEncryption, &$dataKeyId, $keyAltName, $providerName, $masterKey): void { + $keyData = ['keyAltNames' => [$keyAltName]]; + if ($masterKey !== null) { + $keyData['masterKey'] = $masterKey; + } - (new CommandObserver())->observe( - function () use ($clientEncryption, &$localDatakeyId) { - $localDatakeyId = $clientEncryption->createDataKey('local', ['keyAltNames' => ['local_altname']]); - }, - function ($command) use (&$commands) { - $commands[] = $command; - } - ); + $dataKeyId = $clientEncryption->createDataKey($providerName, $keyData); + }, + function ($command) use (&$insertCommand): void { + if ($command['started']->getCommandName() === 'insert') { + $insertCommand = $command['started']->getCommand(); + } + } + ); - $test->assertInstanceOf(Binary::class, $localDatakeyId); - $test->assertSame(Binary::TYPE_UUID, $localDatakeyId->getType()); + $this->assertInstanceOf(Binary::class, $dataKeyId); + $this->assertSame(Binary::TYPE_UUID, $dataKeyId->getType()); - $test->assertCount(2, $commands); - $insert = $commands[1]['started']; - $test->assertSame('insert', $insert->getCommandName()); - $test->assertSame(WriteConcern::MAJORITY, $insert->getCommand()->writeConcern->w); + $this->assertNotNull($insertCommand); + $this->assertObjectHasAttribute('writeConcern', $insertCommand); + $this->assertObjectHasAttribute('w', $insertCommand->writeConcern); + $this->assertSame(WriteConcern::MAJORITY, $insertCommand->writeConcern->w); - $keys = $client->selectCollection('keyvault', 'datakeys')->find(['_id' => $localDatakeyId]); - $keys = iterator_to_array($keys); - $test->assertCount(1, $keys); + $keys = $client->selectCollection('keyvault', 'datakeys')->find(['_id' => $dataKeyId]); + $keys = iterator_to_array($keys); + $this->assertCount(1, $keys); - $key = $keys[0]; - $test->assertNotNull($key); - $test->assertSame('local', $key['masterKey']['provider']); + $key = $keys[0]; + $this->assertNotNull($key); + $this->assertSame($providerName, $key['masterKey']['provider']); - $localEncrypted = $clientEncryption->encrypt('hello local', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $localDatakeyId]); - $test->assertInstanceOf(Binary::class, $localEncrypted); - $test->assertSame(Binary::TYPE_ENCRYPTED, $localEncrypted->getType()); + $encrypted = $clientEncryption->encrypt('hello ' . $providerName, ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $dataKeyId]); + $this->assertInstanceOf(Binary::class, $encrypted); + $this->assertSame(Binary::TYPE_ENCRYPTED, $encrypted->getType()); - $clientEncrypted->selectCollection('db', 'coll')->insertOne(['_id' => 'local', 'value' => $localEncrypted]); - $helloLocal = $clientEncrypted->selectCollection('db', 'coll')->findOne(['_id' => 'local']); - $test->assertNotNull($helloLocal); - $test->assertSame('hello local', $helloLocal['value']); + $clientEncrypted->selectCollection('db', 'coll')->insertOne(['_id' => 'local', 'value' => $encrypted]); + $hello = $clientEncrypted->selectCollection('db', 'coll')->findOne(['_id' => 'local']); + $this->assertNotNull($hello); + $this->assertSame('hello ' . $providerName, $hello['value']); - $localEncryptedAltName = $clientEncryption->encrypt('hello local', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyAltName' => 'local_altname']); - $test->assertEquals($localEncrypted, $localEncryptedAltName); + $encryptedAltName = $clientEncryption->encrypt('hello ' . $providerName, ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyAltName' => $keyAltName]); + $this->assertEquals($encrypted, $encryptedAltName); + + $this->expectException(BulkWriteException::class); + $clientEncrypted->selectCollection('db', 'coll')->insertOne(['encrypted_placeholder' => $encrypted]); + } - $test->expectException(BulkWriteException::class); - $clientEncrypted->selectCollection('db', 'coll')->insertOne(['encrypted_placeholder' => $localEncrypted]); - }, + public static function dataKeyProvider() + { + return [ + 'local' => [ + 'providerName' => 'local', + 'masterKey' => null, ], 'aws' => [ - static function (ClientEncryption $clientEncryption, Client $client, Client $clientEncrypted, self $test) { - $commands = []; - $awsDatakeyId = null; - - (new CommandObserver())->observe( - function () use ($clientEncryption, &$awsDatakeyId) { - $awsDatakeyId = $clientEncryption->createDataKey('aws', ['keyAltNames' => ['aws_altname'], 'masterKey' => ['region' => 'us-east-1', 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0']]); - }, - function ($command) use (&$commands) { - $commands[] = $command; - } - ); - - $test->assertInstanceOf(Binary::class, $awsDatakeyId); - $test->assertSame(Binary::TYPE_UUID, $awsDatakeyId->getType()); - - $test->assertCount(2, $commands); - $insert = $commands[1]['started']; - $test->assertSame('insert', $insert->getCommandName()); - $test->assertSame(WriteConcern::MAJORITY, $insert->getCommand()->writeConcern->w); - - $keys = $client->selectCollection('keyvault', 'datakeys')->find(['_id' => $awsDatakeyId]); - $keys = iterator_to_array($keys); - $test->assertCount(1, $keys); - - $key = $keys[0]; - $test->assertNotNull($key); - $test->assertSame('aws', $key['masterKey']['provider']); - - $awsEncrypted = $clientEncryption->encrypt('hello aws', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $awsDatakeyId]); - $test->assertInstanceOf(Binary::class, $awsEncrypted); - $test->assertSame(Binary::TYPE_ENCRYPTED, $awsEncrypted->getType()); - - $clientEncrypted->selectCollection('db', 'coll')->insertOne(['_id' => 'aws', 'value' => $awsEncrypted]); - $helloAws = $clientEncrypted->selectCollection('db', 'coll')->findOne(['_id' => 'aws']); - $test->assertNotNull($helloAws); - $test->assertSame('hello aws', $helloAws['value']); - - $awsEncryptedAltName = $clientEncryption->encrypt('hello aws', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyAltName' => 'aws_altname']); - $test->assertEquals($awsEncrypted, $awsEncryptedAltName); - - $test->expectException(BulkWriteException::class); - $clientEncrypted->selectCollection('db', 'coll')->insertOne(['encrypted_placeholder' => $awsEncrypted]); - }, + 'providerName' => 'aws', + 'masterKey' => [ + 'region' => 'us-east-1', + 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0', + ], + ], + 'azure' => [ + 'providerName' => 'azure', + 'masterKey' => [ + 'keyVaultEndpoint' => 'key-vault-csfle.vault.azure.net', + 'keyName' => 'key-name-csfle', + ], + ], + 'gcp' => [ + 'providerName' => 'gcp', + 'masterKey' => [ + 'projectId' => 'devprod-drivers', + 'location' => 'global', + 'keyRing' => 'key-ring-csfle', + 'keyName' => 'key-name-csfle', + ], + ], + 'kmip' => [ + 'providerName' => 'kmip', + 'masterKey' => [], ], ]; } /** - * Prose test: External Key Vault + * Prose test 3: External Key Vault * + * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#external-key-vault-test * @testWith [false] * [true] */ - public function testExternalKeyVault($withExternalKeyVault) + public function testExternalKeyVault($withExternalKeyVault): void { - $client = new Client(static::getUri()); - - $client->selectCollection('keyvault', 'datakeys')->drop(); + $client = static::createTestClient(); $client->selectCollection('db', 'coll')->drop(); - $keyId = $client - ->selectCollection('keyvault', 'datakeys') - ->insertOne( - $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/external/external-key.json')), - ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)] - ) - ->getInsertedId(); + self::insertKeyVaultData($client, [ + $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/external/external-key.json')), + ]); $encryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', @@ -334,7 +436,7 @@ public function testExternalKeyVault($withExternalKeyVault) ]; if ($withExternalKeyVault) { - $encryptionOpts['keyVaultClient'] = new Client(static::getUri(), ['username' => 'fake-user', 'password' => 'fake-pwd']); + $encryptionOpts['keyVaultClient'] = static::createTestClient(null, ['username' => 'fake-user', 'password' => 'fake-pwd']); } $autoEncryptionOpts = $encryptionOpts + [ @@ -343,7 +445,7 @@ public function testExternalKeyVault($withExternalKeyVault) ], ]; - $clientEncrypted = new Client(static::getUri(), [], ['autoEncryption' => $autoEncryptionOpts]); + $clientEncrypted = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); $clientEncryption = $clientEncrypted->createClientEncryption($encryptionOpts); try { @@ -366,107 +468,114 @@ public function testExternalKeyVault($withExternalKeyVault) $this->expectException(AuthenticationException::class); } - $clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); + $clientEncryption->encrypt('test', [ + 'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, + 'keyId' => new Binary(base64_decode('LOCALAAAAAAAAAAAAAAAAA=='), Binary::TYPE_UUID), + ]); } public static function provideBSONSizeLimitsAndBatchSplittingTests() { - yield [static function (self $test, Collection $collection) { - // Test 1 - $collection->insertOne(['_id' => 'over_2mib_under_16mib', 'unencrypted' => str_repeat('a', 2097152)]); - $test->assertCollectionCount($collection->getNamespace(), 1); - }, - ]; - - yield [static function (self $test, Collection $collection, array $document) { - // Test 2 - $collection->insertOne( - ['_id' => 'encryption_exceeds_2mib', 'unencrypted' => str_repeat('a', 2097152 - 2000)] + $document - ); - $test->assertCollectionCount($collection->getNamespace(), 1); - }, - ]; - - yield [static function (self $test, Collection $collection) { - // Test 3 - $commands = []; - (new CommandObserver())->observe( - function () use ($collection) { - $collection->insertMany([ - ['_id' => 'over_2mib_1', 'unencrypted' => str_repeat('a', 2097152)], - ['_id' => 'over_2mib_2', 'unencrypted' => str_repeat('a', 2097152)], - ]); - }, - function ($command) use (&$commands) { - $commands[] = $command; - } - ); + yield 'Test 1' => [ + static function (self $test, Collection $collection): void { + $collection->insertOne(['_id' => 'over_2mib_under_16mib', 'unencrypted' => str_repeat('a', 2097152)]); + $test->assertCollectionCount($collection->getNamespace(), 1); + }, + ]; - $test->assertCount(2, $commands); - foreach ($commands as $command) { - $test->assertSame('insert', $command['started']->getCommandName()); - } - }, - ]; - - yield [static function (self $test, Collection $collection, array $document) { - // Test 4 - $commands = []; - (new CommandObserver())->observe( - function () use ($collection, $document) { - $collection->insertMany([ - [ - '_id' => 'encryption_exceeds_2mib_1', - 'unencrypted' => str_repeat('a', 2097152 - 2000), - ] + $document, - [ - '_id' => 'encryption_exceeds_2mib_2', - 'unencrypted' => str_repeat('a', 2097152 - 2000), - ] + $document, - ]); - }, - function ($command) use (&$commands) { - $commands[] = $command; - } - ); + yield 'Test 2' => [ + static function (self $test, Collection $collection, array $document): void { + $collection->insertOne( + ['_id' => 'encryption_exceeds_2mib', 'unencrypted' => str_repeat('a', 2097152 - 2000)] + $document + ); + $test->assertCollectionCount($collection->getNamespace(), 1); + }, + ]; - $test->assertCount(2, $commands); - foreach ($commands as $command) { - $test->assertSame('insert', $command['started']->getCommandName()); - } - }, + yield 'Test 3' => [ + static function (self $test, Collection $collection): void { + $commands = []; + (new CommandObserver())->observe( + function () use ($collection): void { + $collection->insertMany([ + ['_id' => 'over_2mib_1', 'unencrypted' => str_repeat('a', 2097152)], + ['_id' => 'over_2mib_2', 'unencrypted' => str_repeat('a', 2097152)], + ]); + }, + function ($command) use (&$commands): void { + if ($command['started']->getCommandName() !== 'insert') { + return; + } + + $commands[] = $command; + } + ); + + $test->assertCount(2, $commands); + }, + ]; + + yield 'Test 4' => [ + static function (self $test, Collection $collection, array $document): void { + $commands = []; + (new CommandObserver())->observe( + function () use ($collection, $document): void { + $collection->insertMany([ + [ + '_id' => 'encryption_exceeds_2mib_1', + 'unencrypted' => str_repeat('a', 2097152 - 2000), + ] + $document, + [ + '_id' => 'encryption_exceeds_2mib_2', + 'unencrypted' => str_repeat('a', 2097152 - 2000), + ] + $document, + ]); + }, + function ($command) use (&$commands): void { + if ($command['started']->getCommandName() !== 'insert') { + return; + } + + $commands[] = $command; + } + ); + + $test->assertCount(2, $commands); + }, ]; - yield [static function (self $test, Collection $collection) { - // Test 5 - $collection->insertOne(['_id' => 'under_16mib', 'unencrypted' => str_repeat('a', 16777216 - 2000)]); - $test->assertCollectionCount($collection->getNamespace(), 1); - }, + yield 'Test 5' => [ + static function (self $test, Collection $collection): void { + $collection->insertOne(['_id' => 'under_16mib', 'unencrypted' => str_repeat('a', 16777216 - 2000)]); + $test->assertCollectionCount($collection->getNamespace(), 1); + }, ]; - yield [static function (self $test, Collection $collection, array $document) { - // Test 6 - $test->expectException(BulkWriteException::class); - $test->expectExceptionMessageMatches('#object to insert too large#'); - $collection->insertOne(['_id' => 'encryption_exceeds_16mib', 'unencrypted' => str_repeat('a', 16777216 - 2000)] + $document); - }, + yield 'Test 6' => [ + static function (self $test, Collection $collection, array $document): void { + $test->expectException(BulkWriteException::class); + $test->expectExceptionMessageMatches('#object to insert too large#'); + $collection->insertOne(['_id' => 'encryption_exceeds_16mib', 'unencrypted' => str_repeat('a', 16777216 - 2000)] + $document); + }, ]; } /** - * Prose test: BSON size limits and batch splitting + * Prose test 4: BSON Size Limits and Batch Splitting * + * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#bson-size-limits-and-batch-splitting * @dataProvider provideBSONSizeLimitsAndBatchSplittingTests */ - public function testBSONSizeLimitsAndBatchSplitting(Closure $test) + public function testBSONSizeLimitsAndBatchSplitting(Closure $test): void { - $client = new Client(static::getUri()); + $client = static::createTestClient(); - $client->selectCollection('keyvault', 'datakeys')->drop(); $client->selectCollection('db', 'coll')->drop(); - $client->selectDatabase('db')->createCollection('coll', ['validator' => ['$jsonSchema' => $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/limits/limits-schema.json'))]]); - $client->selectCollection('keyvault', 'datakeys')->insertOne($this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/limits/limits-key.json'))); + + self::insertKeyVaultData($client, [ + $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/limits/limits-key.json')), + ]); $autoEncryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', @@ -476,7 +585,7 @@ public function testBSONSizeLimitsAndBatchSplitting(Closure $test) 'keyVaultClient' => $client, ]; - $clientEncrypted = new Client(static::getUri(), [], ['autoEncryption' => $autoEncryptionOpts]); + $clientEncrypted = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); $collection = $clientEncrypted->selectCollection('db', 'coll'); @@ -486,11 +595,13 @@ public function testBSONSizeLimitsAndBatchSplitting(Closure $test) } /** - * Prose test: Views are prohibited + * Prose test 5: Views Are Prohibited + * + * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#views-are-prohibited */ - public function testViewsAreProhibited() + public function testViewsAreProhibited(): void { - $client = new Client(static::getUri()); + $client = static::createTestClient(); $client->selectCollection('db', 'view')->drop(); $client->selectDatabase('db')->command(['create' => 'view', 'viewOn' => 'coll']); @@ -502,7 +613,7 @@ public function testViewsAreProhibited() ], ]; - $clientEncrypted = new Client(static::getUri(), [], ['autoEncryption' => $autoEncryptionOpts]); + $clientEncrypted = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); try { $clientEncrypted->selectCollection('db', 'view')->insertOne(['foo' => 'bar']); @@ -516,36 +627,42 @@ public function testViewsAreProhibited() } /** - * Prose test: BSON Corpus + * Prose test 6: BSON Corpus * + * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#corpus-test * @testWith [true] * [false] */ - public function testCorpus($schemaMap = true) + public function testCorpus($schemaMap = true): void { - $client = new Client(static::getUri()); - + $client = static::createTestClient(); $client->selectDatabase('db')->dropCollection('coll'); $schema = $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-schema.json')); if (! $schemaMap) { - $client - ->selectDatabase('db') - ->createCollection('coll', ['validator' => ['$jsonSchema' => $schema]]); + $client->selectDatabase('db')->createCollection('coll', ['validator' => ['$jsonSchema' => $schema]]); } - $client->selectDatabase('keyvault')->dropCollection('datakeys'); - $client->selectCollection('keyvault', 'datakeys')->insertMany([ + self::insertKeyVaultData($client, [ $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-local.json')), $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-aws.json')), + $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-azure.json')), + $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-gcp.json')), + $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-kmip.json')), ]); $encryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', 'kmsProviders' => [ 'aws' => Context::getAWSCredentials(), + 'azure' => Context::getAzureCredentials(), + 'gcp' => Context::getGCPCredentials(), 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + 'kmip' => ['endpoint' => Context::getKmipEndpoint()], + ], + 'tlsOptions' => [ + 'kmip' => Context::getKmsTlsOptions(), ], ]; @@ -560,92 +677,282 @@ public function testCorpus($schemaMap = true) $corpus = (array) $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus.json')); $corpusCopied = []; - $clientEncrypted = new Client(static::getUri(), [], ['autoEncryption' => $autoEncryptionOpts]); + $clientEncrypted = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); $clientEncryption = $clientEncrypted->createClientEncryption($encryptionOpts); $collection = $clientEncrypted->selectCollection('db', 'coll'); + $unpreparedFieldNames = [ + '_id', + 'altname_aws', + 'altname_azure', + 'altname_gcp', + 'altname_local', + 'altname_kmip', + ]; + foreach ($corpus as $fieldName => $data) { - switch ($fieldName) { - case '_id': - case 'altname_aws': - case 'altname_local': - $corpusCopied[$fieldName] = $data; - break; - - default: - $corpusCopied[$fieldName] = $this->prepareCorpusData($data, $clientEncryption); + if (in_array($fieldName, $unpreparedFieldNames, true)) { + $corpusCopied[$fieldName] = $data; + continue; } + + $corpusCopied[$fieldName] = $this->prepareCorpusData($fieldName, $data, $clientEncryption); } $collection->insertOne($corpusCopied); $corpusDecrypted = $collection->findOne(['_id' => 'client_side_encryption_corpus']); $this->assertDocumentsMatch($corpus, $corpusDecrypted); + + $corpusEncryptedExpected = (array) $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-encrypted.json')); + $corpusEncryptedActual = $client->selectCollection('db', 'coll')->findOne(['_id' => 'client_side_encryption_corpus'], ['typeMap' => ['root' => 'array', 'document' => stdClass::class, 'array' => 'array']]); + + foreach ($corpusEncryptedExpected as $fieldName => $expectedData) { + if (in_array($fieldName, $unpreparedFieldNames, true)) { + continue; + } + + $actualData = $corpusEncryptedActual[$fieldName]; + + if ($expectedData->algo === 'det') { + $this->assertEquals($expectedData->value, $actualData->value, 'Value for field ' . $fieldName . ' does not match expected value.'); + } + + if ($expectedData->allowed) { + if ($expectedData->algo === 'rand') { + $this->assertNotEquals($expectedData->value, $actualData->value, 'Value for field ' . $fieldName . ' does not differ from expected value.'); + } + + $this->assertEquals( + $clientEncryption->decrypt($expectedData->value), + $clientEncryption->decrypt($actualData->value), + 'Decrypted value for field ' . $fieldName . ' does not match.' + ); + } else { + $this->assertEquals($corpus[$fieldName]->value, $actualData->value, 'Value for field ' . $fieldName . ' does not match original value.'); + } + } } /** - * Prose test: Custom Endpoint + * Prose test 7: Custom Endpoint + * + * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#custom-endpoint-test + * @dataProvider customEndpointProvider */ - public function testCustomEndpoint() + public function testCustomEndpoint(Closure $test): void { - // Test 1 - $client = new Client(static::getUri()); + $client = static::createTestClient(); - $encryptionOpts = [ + $clientEncryption = $client->createClientEncryption([ 'keyVaultNamespace' => 'keyvault.datakeys', 'kmsProviders' => [ 'aws' => Context::getAWSCredentials(), + 'azure' => Context::getAzureCredentials() + ['identityPlatformEndpoint' => 'login.microsoftonline.com:443'], + 'gcp' => Context::getGCPCredentials() + ['endpoint' => 'oauth2.googleapis.com:443'], + 'kmip' => ['endpoint' => Context::getKmipEndpoint()], + ], + 'tlsOptions' => [ + 'kmip' => Context::getKmsTlsOptions(), ], + ]); + + $clientEncryptionInvalid = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'azure' => Context::getAzureCredentials() + ['identityPlatformEndpoint' => 'doesnotexist.invalid:443'], + 'gcp' => Context::getGCPCredentials() + ['endpoint' => 'doesnotexist.invalid:443'], + 'kmip' => ['endpoint' => 'doesnotexist.local:5698'], + ], + 'tlsOptions' => [ + 'kmip' => Context::getKmsTlsOptions(), + ], + ]); + + $test($this, $clientEncryption, $clientEncryptionInvalid); + } + + public static function customEndpointProvider() + { + $awsMasterKey = ['region' => 'us-east-1', 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0']; + $azureMasterKey = ['keyVaultEndpoint' => 'key-vault-csfle.vault.azure.net', 'keyName' => 'key-name-csfle']; + $gcpMasterKey = [ + 'projectId' => 'devprod-drivers', + 'location' => 'global', + 'keyRing' => 'key-ring-csfle', + 'keyName' => 'key-name-csfle', + 'endpoint' => 'cloudkms.googleapis.com:443', + ]; + $kmipMasterKey = ['keyId' => '1']; + + yield 'Test 1' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($awsMasterKey): void { + $keyId = $clientEncryption->createDataKey('aws', ['masterKey' => $awsMasterKey]); + $encrypted = $clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); + $test->assertSame('test', $clientEncryption->decrypt($encrypted)); + }, + ]; + + yield 'Test 2' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($awsMasterKey): void { + $keyId = $clientEncryption->createDataKey('aws', ['masterKey' => $awsMasterKey + ['endpoint' => 'kms.us-east-1.amazonaws.com']]); + $encrypted = $clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); + $test->assertSame('test', $clientEncryption->decrypt($encrypted)); + }, + ]; + + yield 'Test 3' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($awsMasterKey): void { + $keyId = $clientEncryption->createDataKey('aws', ['masterKey' => $awsMasterKey + [ 'endpoint' => 'kms.us-east-1.amazonaws.com:443']]); + $encrypted = $clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); + $test->assertSame('test', $clientEncryption->decrypt($encrypted)); + }, ]; - $clientEncryption = $client->createClientEncryption($encryptionOpts); + yield 'Test 4' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($awsMasterKey): void { + $test->expectException(ConnectionException::class); + $clientEncryption->createDataKey('aws', ['masterKey' => $awsMasterKey + ['endpoint' => 'kms.us-east-1.amazonaws.com:12345']]); + }, + ]; - // Test 2 - $masterKeyConfig = ['region' => 'us-east-1', 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0']; - $keyId = $clientEncryption->createDataKey('aws', ['masterKey' => $masterKeyConfig]); - $encrypted = $clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); - $this->assertSame('test', $clientEncryption->decrypt($encrypted)); + yield 'Test 5' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($awsMasterKey): void { + $test->expectException(RuntimeException::class); + $clientEncryption->createDataKey('aws', ['masterKey' => $awsMasterKey + ['endpoint' => 'kms.us-east-2.amazonaws.com']]); + }, + ]; - // Test 3 - $keyId = $clientEncryption->createDataKey('aws', ['masterKey' => $masterKeyConfig + ['endpoint' => 'kms.us-east-1.amazonaws.com']]); - $encrypted = $clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); - $this->assertSame('test', $clientEncryption->decrypt($encrypted)); + yield 'Test 6' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($awsMasterKey): void { + $test->expectException(RuntimeException::class); + $test->expectExceptionMessageMatches('#doesnotexist.invalid#'); + $clientEncryption->createDataKey('aws', ['masterKey' => $awsMasterKey + ['endpoint' => 'doesnotexist.invalid']]); + }, + ]; - // Test 4 - $keyId = $clientEncryption->createDataKey('aws', ['masterKey' => $masterKeyConfig + [ 'endpoint' => 'kms.us-east-1.amazonaws.com:443']]); - $encrypted = $clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); - $this->assertSame('test', $clientEncryption->decrypt($encrypted)); + yield 'Test 7' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($azureMasterKey): void { + $keyId = $clientEncryption->createDataKey('azure', ['masterKey' => $azureMasterKey]); + $encrypted = $clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); + $test->assertSame('test', $clientEncryption->decrypt($encrypted)); - // Test 5 - try { - $clientEncryption->createDataKey('aws', ['masterKey' => $masterKeyConfig + [ 'endpoint' => 'kms.us-east-1.amazonaws.com:12345']]); - $this->fail('Expected exception to be thrown'); - } catch (ConnectionException $e) { - } + $test->expectException(RuntimeException::class); + $test->expectExceptionMessageMatches('#doesnotexist.invalid#'); + $clientEncryptionInvalid->createDataKey('azure', ['masterKey' => $azureMasterKey]); + }, + ]; - // Test 6 - try { - $clientEncryption->createDataKey('aws', ['masterKey' => $masterKeyConfig + [ 'endpoint' => 'kms.us-east-2.amazonaws.com']]); - $this->fail('Expected exception to be thrown'); - } catch (RuntimeException $e) { - $this->assertStringContainsString('us-east-1', $e->getMessage()); - } + yield 'Test 8' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($gcpMasterKey): void { + $keyId = $clientEncryption->createDataKey('gcp', ['masterKey' => $gcpMasterKey]); + $encrypted = $clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); + $test->assertSame('test', $clientEncryption->decrypt($encrypted)); - // Test 7 - try { - $clientEncryption->createDataKey('aws', ['masterKey' => $masterKeyConfig + [ 'endpoint' => 'example.com']]); - $this->fail('Expected exception to be thrown'); - } catch (RuntimeException $e) { - $this->assertStringContainsString('parse error', $e->getMessage()); + $test->expectException(RuntimeException::class); + $test->expectExceptionMessageMatches('#doesnotexist.invalid#'); + $clientEncryptionInvalid->createDataKey('gcp', ['masterKey' => $gcpMasterKey]); + }, + ]; + + yield 'Test 9' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($gcpMasterKey): void { + $masterKey = $gcpMasterKey; + $masterKey['endpoint'] = 'doesnotexist.invalid:443'; + + $test->expectException(RuntimeException::class); + $test->expectExceptionMessageMatches('#Invalid KMS response#'); + $clientEncryption->createDataKey('gcp', ['masterKey' => $masterKey]); + }, + ]; + + yield 'Test 10' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($kmipMasterKey): void { + $keyId = $clientEncryption->createDataKey('kmip', ['masterKey' => $kmipMasterKey]); + $encrypted = $clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); + $test->assertSame('test', $clientEncryption->decrypt($encrypted)); + + $test->expectException(RuntimeException::class); + $test->expectExceptionMessageMatches('#doesnotexist.local#'); + $clientEncryptionInvalid->createDataKey('kmip', ['masterKey' => $kmipMasterKey]); + }, + ]; + + yield 'Test 11' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($kmipMasterKey): void { + $kmipMasterKey['endpoint'] = Context::getKmipEndpoint(); + + $keyId = $clientEncryption->createDataKey('kmip', ['masterKey' => $kmipMasterKey]); + $encrypted = $clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); + $test->assertSame('test', $clientEncryption->decrypt($encrypted)); + }, + ]; + + yield 'Test 12' => [ + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($kmipMasterKey): void { + $kmipMasterKey['endpoint'] = 'doesnotexist.local:5698'; + + $test->expectException(RuntimeException::class); + $test->expectExceptionMessageMatches('#doesnotexist.local#'); + $clientEncryption->createDataKey('kmip', ['masterKey' => $kmipMasterKey]); + }, + ]; + } + + /** + * Prose test 8: Bypass Spawning mongocryptd (via loading shared library) + * + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/#via-loading-shared-library + */ + public function testBypassSpawningMongocryptdViaLoadingSharedLibrary(): void + { + if (! static::isCryptSharedLibAvailable()) { + $this->markTestSkipped('Bypass spawning of mongocryptd cannot be tested when crypt_shared is not available'); } + + $autoEncryptionOpts = [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + ], + 'schemaMap' => [ + 'db.coll' => $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/external/external-schema.json')), + ], + 'extraOptions' => [ + 'mongocryptdBypassSpawn' => true, + 'mongocryptdURI' => 'mongodb://localhost:27021/?serverSelectionTimeoutMS=1000', + 'mongocryptdSpawnArgs' => ['--pidfilepath=bypass-spawning-mongocryptd.pid', '--port=27021'], + 'cryptSharedLibRequired' => true, + ], + ]; + + $clientEncrypted = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); + + $clientEncrypted->selectCollection('db', 'coll')->insertOne(['unencrypted' => 'test']); + + $clientMongocryptd = static::createTestClient('mongodb://localhost:27021/?serverSelectionTimeoutMS=1000'); + + $this->expectException(ConnectionTimeoutException::class); + $this->expectExceptionMessageMatches('#(No suitable servers found)|(No servers yet eligible for rescan)#'); + $clientMongocryptd->getManager()->selectServer(); } /** - * Prose test: Bypass spawning mongocryptd (via mongocryptdBypassSpawn) + * Prose test 8: Bypass Spawning mongocryptd (via mongocryptdBypassSpawn) + * + * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#via-mongocryptdbypassspawn */ - public function testBypassSpawningMongocryptdViaBypassSpawn() + public function testBypassSpawningMongocryptdViaBypassSpawn(): void { + /* If crypt_shared is available it will likely already have been loaded + * by a previous test so there is no way to prevent it from being used. + * Since CSFLE prefers crypt_shared to mongocryptd there is reason to + * run any of the "bypass spawning" tests (see also: MONGOCRYPT-421). */ + if (static::isCryptSharedLibAvailable()) { + $this->markTestSkipped('Bypass spawning of mongocryptd cannot be tested when crypt_shared is available'); + } + $autoEncryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', 'kmsProviders' => [ @@ -656,12 +963,13 @@ public function testBypassSpawningMongocryptdViaBypassSpawn() ], 'extraOptions' => [ 'mongocryptdBypassSpawn' => true, - 'mongocryptdURI' => 'mongodb://localhost:27021/db?serverSelectionTimeoutMS=1000', + 'mongocryptdURI' => 'mongodb://localhost:27021/?serverSelectionTimeoutMS=1000', 'mongocryptdSpawnArgs' => ['--pidfilepath=bypass-spawning-mongocryptd.pid', '--port=27021'], ], ]; - $clientEncrypted = new Client(static::getUri(), [], ['autoEncryption' => $autoEncryptionOpts]); + // Disable adding cryptSharedLibPath, as it may interfere with this test + $clientEncrypted = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); try { $clientEncrypted->selectCollection('db', 'coll')->insertOne(['encrypted' => 'test']); @@ -675,10 +983,16 @@ public function testBypassSpawningMongocryptdViaBypassSpawn() } /** - * Bypass spawning mongocryptd (via bypassAutoEncryption) + * Prose test 8: Bypass spawning mongocryptd (via bypassAutoEncryption) + * + * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#via-bypassautoencryption */ - public function testBypassSpawningMongocryptdViaBypassAutoEncryption() + public function testBypassSpawningMongocryptdViaBypassAutoEncryption(): void { + if (static::isCryptSharedLibAvailable()) { + $this->markTestSkipped('Bypass spawning of mongocryptd cannot be tested when crypt_shared is available'); + } + $autoEncryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', 'kmsProviders' => [ @@ -690,109 +1004,1012 @@ public function testBypassSpawningMongocryptdViaBypassAutoEncryption() ], ]; - $clientEncrypted = new Client(static::getUri(), [], ['autoEncryption' => $autoEncryptionOpts]); + // Disable adding cryptSharedLibPath, as it may interfere with this test + $clientEncrypted = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); - $clientEncrypted->selectCollection('db', 'coll')->insertOne(['encrypted' => 'test']); + $clientEncrypted->selectCollection('db', 'coll')->insertOne(['unencrypted' => 'test']); - $clientMongocryptd = new Client('mongodb://localhost:27021'); + $clientMongocryptd = static::createTestClient('mongodb://localhost:27021/?serverSelectionTimeoutMS=1000'); $this->expectException(ConnectionTimeoutException::class); - $clientMongocryptd->selectDatabase('db')->command(['isMaster' => true]); + $this->expectExceptionMessageMatches('#(No suitable servers found)|(No servers yet eligible for rescan)#'); + $clientMongocryptd->getManager()->selectServer(); } /** - * Casts the value for a BSON corpus structure to int64 if necessary. - * - * This is a workaround for an issue in mongocryptd which refuses to encrypt - * int32 values if the schemaMap defines a "long" bsonType for an object. - * - * @param object $data + * Prose test 8: Bypass spawning mongocryptd (via bypassQueryAnalysis) * - * @return Int64|mixed + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#via-bypassqueryanalysis */ - private function craftInt64($data) + public function testBypassSpawningMongocryptdViaBypassQueryAnalysis(): void { - if ($data->type !== 'long' || $data->value instanceof Int64) { - return $data->value; + if (static::isCryptSharedLibAvailable()) { + $this->markTestSkipped('Bypass spawning of mongocryptd cannot be tested when crypt_shared is available'); } - $class = Int64::class; + $autoEncryptionOpts = [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + ], + 'bypassQueryAnalysis' => true, + 'extraOptions' => [ + 'mongocryptdSpawnArgs' => ['--pidfilepath=bypass-spawning-mongocryptd.pid', '--port=27021'], + ], + ]; + + // Disable adding cryptSharedLibPath, as it may interfere with this test + $clientEncrypted = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); - $intAsString = sprintf((string) $data->value); - $array = sprintf('a:1:{s:7:"integer";s:%d:"%s";}', strlen($intAsString), $intAsString); - $int64 = sprintf('C:%d:"%s":%d:{%s}', strlen($class), $class, strlen($array), $array); + $clientEncrypted->selectCollection('db', 'coll')->insertOne(['unencrypted' => 'test']); - return unserialize($int64); - } + $clientMongocryptd = static::createTestClient('mongodb://localhost:27021/?serverSelectionTimeoutMS=1000'); - private function createTestCollection($jsonSchema) - { - $options = empty($jsonSchema) ? [] : ['validator' => ['$jsonSchema' => $jsonSchema]]; - $operation = new CreateCollection($this->getContext()->databaseName, $this->getContext()->collectionName, $options); - $operation->execute($this->getPrimaryServer()); + $this->expectException(ConnectionTimeoutException::class); + $this->expectExceptionMessageMatches('#(No suitable servers found)|(No servers yet eligible for rescan)#'); + $clientMongocryptd->getManager()->selectServer(); } - private function encryptCorpusValue(stdClass $data, ClientEncryption $clientEncryption) + /** + * Prose test 10: KMS TLS Tests (Invalid KMS Certificate) + * + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#invalid-kms-certificate + */ + public function testInvalidKmsCertificate(): void { - $encryptionOptions = [ - 'algorithm' => $data->algo === 'rand' ? ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_RANDOM : ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, - ]; - - switch ($data->identifier) { - case 'id': - $keyId = $data->kms === 'local' ? 'LOCALAAAAAAAAAAAAAAAAA==' : 'AWSAAAAAAAAAAAAAAAAAAA=='; - $encryptionOptions['keyId'] = new Binary(base64_decode($keyId), 4); - break; - - case 'altname': - $encryptionOptions['keyAltName'] = $data->kms === 'local' ? 'local' : 'aws'; - break; - - default: - throw new UnexpectedValueException('Unexpected value "%s" for identifier', $data->identifier); - } - - if ($data->allowed) { - $encrypted = $clientEncryption->encrypt($this->craftInt64($data), $encryptionOptions); - $this->assertEquals($data->value, $clientEncryption->decrypt($encrypted)); + $client = static::createTestClient(); - return $encrypted; - } + $clientEncryption = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['aws' => Context::getAWSCredentials()], + 'tlsOptions' => ['aws' => Context::getKmsTlsOptions()], + ]); - try { - $clientEncryption->encrypt($data->value, $encryptionOptions); - $this->fail('Expected exception to be thrown'); - } catch (RuntimeException $e) { - } + $this->expectException(ConnectionException::class); + // Note: this assumes an OpenSSL error message + $this->expectExceptionMessageMatches('#certificate has expired#'); - return $data->value; + $clientEncryption->createDataKey('aws', [ + 'masterKey' => [ + 'region' => 'us-east-1', + 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0', + 'endpoint' => self::getEnv('KMS_ENDPOINT_EXPIRED'), + ], + ]); } - private function insertKeyVaultData(array $keyVaultData = null) + /** + * Prose test 10: KMS TLS Tests (Invalid Hostname in KMS Certificate) + * + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#invalid-hostname-in-kms-certificate + */ + public function testInvalidHostnameInKmsCertificate(): void { - if (empty($keyVaultData)) { - return; - } + $client = static::createTestClient(); - $context = $this->getContext(); - $collection = $context->selectCollection('keyvault', 'datakeys', ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)] + $context->defaultWriteOptions); - $collection->drop(); - $collection->insertMany($keyVaultData); + $clientEncryption = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['aws' => Context::getAWSCredentials()], + 'tlsOptions' => ['aws' => Context::getKmsTlsOptions()], + ]); - return; + $this->expectException(ConnectionException::class); + // Note: this assumes an OpenSSL error message + $this->expectExceptionMessageMatches('#IP address mismatch#'); + + $clientEncryption->createDataKey('aws', [ + 'masterKey' => [ + 'region' => 'us-east-1', + 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0', + 'endpoint' => self::getEnv('KMS_ENDPOINT_WRONG_HOST'), + ], + ]); } - private function prepareCorpusData(stdClass $data, ClientEncryption $clientEncryption) + /** + * Prose test 11: KMS TLS Options + * + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#kms-tls-options-tests + * @dataProvider provideKmsTlsOptionsTests + */ + public function testKmsTlsOptions(Closure $test): void { - if ($data->method === 'auto') { - $data->value = $this->craftInt64($data); + $client = static::createTestClient(); - return $data; - } + $clientEncryptionNoClientCert = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'aws' => Context::getAWSCredentials(), + 'azure' => Context::getAzureCredentials() + ['identityPlatformEndpoint' => self::getEnv('KMS_ENDPOINT_REQUIRE_CLIENT_CERT')], + 'gcp' => Context::getGCPCredentials() + ['endpoint' => self::getEnv('KMS_ENDPOINT_REQUIRE_CLIENT_CERT')], + 'kmip' => ['endpoint' => Context::getKmipEndpoint()], + ], + 'tlsOptions' => [ + 'aws' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + 'azure' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + 'gcp' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + 'kmip' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + ], + ]); - $returnData = clone $data; - $returnData->value = $this->encryptCorpusValue($data, $clientEncryption); + $clientEncryptionWithTls = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'aws' => Context::getAWSCredentials(), + 'azure' => Context::getAzureCredentials() + ['identityPlatformEndpoint' => self::getEnv('KMS_ENDPOINT_REQUIRE_CLIENT_CERT')], + 'gcp' => Context::getGCPCredentials() + ['endpoint' => self::getEnv('KMS_ENDPOINT_REQUIRE_CLIENT_CERT')], + 'kmip' => ['endpoint' => Context::getKmipEndpoint()], + ], + 'tlsOptions' => [ + 'aws' => Context::getKmsTlsOptions(), + 'azure' => Context::getKmsTlsOptions(), + 'gcp' => Context::getKmsTlsOptions(), + 'kmip' => Context::getKmsTlsOptions(), + ], + ]); - return $data->allowed ? $returnData : $data; + $clientEncryptionExpired = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'aws' => Context::getAWSCredentials(), + 'azure' => Context::getAzureCredentials() + ['identityPlatformEndpoint' => self::getEnv('KMS_ENDPOINT_EXPIRED')], + 'gcp' => Context::getGCPCredentials() + ['endpoint' => self::getEnv('KMS_ENDPOINT_EXPIRED')], + 'kmip' => ['endpoint' => self::getEnv('KMS_ENDPOINT_EXPIRED')], + ], + 'tlsOptions' => [ + 'aws' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + 'azure' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + 'gcp' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + 'kmip' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + ], + ]); + + $clientEncryptionInvalidHostname = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'aws' => Context::getAWSCredentials(), + 'azure' => Context::getAzureCredentials() + ['identityPlatformEndpoint' => self::getEnv('KMS_ENDPOINT_WRONG_HOST')], + 'gcp' => Context::getGCPCredentials() + ['endpoint' => self::getEnv('KMS_ENDPOINT_WRONG_HOST')], + 'kmip' => ['endpoint' => self::getEnv('KMS_ENDPOINT_WRONG_HOST')], + ], + 'tlsOptions' => [ + 'aws' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + 'azure' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + 'gcp' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + 'kmip' => ['tlsCAFile' => getenv('KMS_TLS_CA_FILE')], + ], + ]); + + $test($this, $clientEncryptionNoClientCert, $clientEncryptionWithTls, $clientEncryptionExpired, $clientEncryptionInvalidHostname); + } + + public static function provideKmsTlsOptionsTests() + { + $awsMasterKey = ['region' => 'us-east-1', 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0']; + $azureMasterKey = ['keyVaultEndpoint' => 'doesnotexist.local', 'keyName' => 'foo']; + $gcpMasterKey = ['projectId' => 'foo', 'location' => 'bar', 'keyRing' => 'baz', 'keyName' => 'foo']; + $kmipMasterKey = []; + + // Note: expected exception messages below assume OpenSSL is used + + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-1-aws + yield 'AWS: client_encryption_no_client_cert' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($awsMasterKey): void { + $test->expectException(ConnectionException::class); + // Avoid asserting exception message for failed TLS handshake since it may be inconsistent + $clientEncryptionNoClientCert->createDataKey('aws', ['masterKey' => $awsMasterKey + ['endpoint' => self::getEnv('KMS_ENDPOINT_REQUIRE_CLIENT_CERT')]]); + }, + ]; + + yield 'AWS: client_encryption_with_tls' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($awsMasterKey): void { + $test->expectException(EncryptionException::class); + $test->expectExceptionMessageMatches('#parse error#'); + $clientEncryptionWithTls->createDataKey('aws', ['masterKey' => $awsMasterKey + ['endpoint' => self::getEnv('KMS_ENDPOINT_REQUIRE_CLIENT_CERT')]]); + }, + ]; + + yield 'AWS: client_encryption_expired' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($awsMasterKey): void { + $test->expectException(ConnectionException::class); + $test->expectExceptionMessageMatches('#certificate has expired#'); + $clientEncryptionExpired->createDataKey('aws', ['masterKey' => $awsMasterKey + ['endpoint' => self::getEnv('KMS_ENDPOINT_EXPIRED')]]); + }, + ]; + + yield 'AWS: client_encryption_invalid_hostname' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($awsMasterKey): void { + $test->expectException(ConnectionException::class); + $test->expectExceptionMessageMatches('#IP address mismatch#'); + $clientEncryptionInvalidHostname->createDataKey('aws', ['masterKey' => $awsMasterKey + ['endpoint' => self::getEnv('KMS_ENDPOINT_WRONG_HOST')]]); + }, + ]; + + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-2-azure + yield 'Azure: client_encryption_no_client_cert' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($azureMasterKey): void { + $test->expectException(ConnectionException::class); + // Avoid asserting exception message for failed TLS handshake since it may be inconsistent + $clientEncryptionNoClientCert->createDataKey('azure', ['masterKey' => $azureMasterKey]); + }, + ]; + + yield 'Azure: client_encryption_with_tls' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($azureMasterKey): void { + $test->expectException(EncryptionException::class); + $test->expectExceptionMessageMatches('#HTTP status=404#'); + $clientEncryptionWithTls->createDataKey('azure', ['masterKey' => $azureMasterKey]); + }, + ]; + + yield 'Azure: client_encryption_expired' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($azureMasterKey): void { + $test->expectException(ConnectionException::class); + $test->expectExceptionMessageMatches('#certificate has expired#'); + $clientEncryptionExpired->createDataKey('azure', ['masterKey' => $azureMasterKey]); + }, + ]; + + yield 'Azure: client_encryption_invalid_hostname' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($azureMasterKey): void { + $test->expectException(ConnectionException::class); + $test->expectExceptionMessageMatches('#IP address mismatch#'); + $clientEncryptionInvalidHostname->createDataKey('azure', ['masterKey' => $azureMasterKey]); + }, + ]; + + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-3-gcp + yield 'GCP: client_encryption_no_client_cert' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($gcpMasterKey): void { + $test->expectException(ConnectionException::class); + // Avoid asserting exception message for failed TLS handshake since it may be inconsistent + $clientEncryptionNoClientCert->createDataKey('gcp', ['masterKey' => $gcpMasterKey]); + }, + ]; + + yield 'GCP: client_encryption_with_tls' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($gcpMasterKey): void { + $test->expectException(EncryptionException::class); + $test->expectExceptionMessageMatches('#HTTP status=404#'); + $clientEncryptionWithTls->createDataKey('gcp', ['masterKey' => $gcpMasterKey]); + }, + ]; + + yield 'GCP: client_encryption_expired' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($gcpMasterKey): void { + $test->expectException(ConnectionException::class); + $test->expectExceptionMessageMatches('#certificate has expired#'); + $clientEncryptionExpired->createDataKey('gcp', ['masterKey' => $gcpMasterKey]); + }, + ]; + + yield 'GCP: client_encryption_invalid_hostname' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($gcpMasterKey): void { + $test->expectException(ConnectionException::class); + $test->expectExceptionMessageMatches('#IP address mismatch#'); + $clientEncryptionInvalidHostname->createDataKey('gcp', ['masterKey' => $gcpMasterKey]); + }, + ]; + + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-4-kmip + yield 'KMIP: client_encryption_no_client_cert' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($kmipMasterKey): void { + $test->expectException(ConnectionException::class); + // Avoid asserting exception message for failed TLS handshake since it may be inconsistent + $clientEncryptionNoClientCert->createDataKey('kmip', ['masterKey' => $kmipMasterKey]); + }, + ]; + + yield 'KMIP: client_encryption_with_tls' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($kmipMasterKey): void { + $keyId = $clientEncryptionWithTls->createDataKey('kmip', ['masterKey' => $kmipMasterKey]); + $test->assertInstanceOf(Binary::class, $keyId); + }, + ]; + + yield 'KMIP: client_encryption_expired' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($kmipMasterKey): void { + $test->expectException(ConnectionException::class); + $test->expectExceptionMessageMatches('#certificate has expired#'); + $clientEncryptionExpired->createDataKey('kmip', ['masterKey' => $kmipMasterKey]); + }, + ]; + + yield 'KMIP: client_encryption_invalid_hostname' => [ + static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($kmipMasterKey): void { + $test->expectException(ConnectionException::class); + $test->expectExceptionMessageMatches('#IP address mismatch#'); + $clientEncryptionInvalidHostname->createDataKey('kmip', ['masterKey' => $kmipMasterKey]); + }, + ]; + } + + /** + * Prose test 12: Explicit Encryption + * + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#explicit-encryption + * @dataProvider provideExplicitEncryptionTests + */ + public function testExplicitEncryption(Closure $test): void + { + if ($this->isStandalone() || ($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets())) { + $this->markTestSkipped('Explicit encryption tests require replica sets'); + } + + $this->skipIfServerVersion('<', '7.0.0', 'Explicit encryption tests require MongoDB 7.0 or later'); + + // Test setup + $encryptedFields = $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/etc/data/encryptedFields.json')); + $key1Document = $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/etc/data/keys/key1-document.json')); + $key1Id = $key1Document->_id; + + $client = static::createTestClient(); + + $database = $client->selectDatabase('db'); + $database->dropCollection('explicit_encryption', ['encryptedFields' => $encryptedFields]); + $database->createCollection('explicit_encryption', ['encryptedFields' => $encryptedFields]); + + $database = $client->selectDatabase('keyvault'); + $database->dropCollection('datakeys'); + $database->createCollection('datakeys'); + + $client->selectCollection('keyvault', 'datakeys')->insertOne($key1Document, ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]); + + $keyVaultClient = static::createTestClient(); + + $clientEncryption = new ClientEncryption([ + 'keyVaultClient' => $keyVaultClient->getManager(), + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + ]); + + $autoEncryptionOpts = [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + 'bypassQueryAnalysis' => true, + ]; + + $encryptedClient = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); + + $test($this, $clientEncryption, $encryptedClient, $keyVaultClient, $key1Id); + } + + public static function provideExplicitEncryptionTests() + { + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-1-can-insert-encrypted-indexed-and-find + yield 'Case 1: can insert encrypted indexed and find' => [ + static function (self $test, ClientEncryption $clientEncryption, Client $encryptedClient, Client $keyVaultClient, Binary $key1Id): void { + $value = 'encrypted indexed value'; + + $insertPayload = $clientEncryption->encrypt($value, [ + 'keyId' => $key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_INDEXED, + 'contentionFactor' => 0, + ]); + + $collection = $encryptedClient->selectCollection('db', 'explicit_encryption'); + $collection->insertOne(['encryptedIndexed' => $insertPayload]); + + $findPayload = $clientEncryption->encrypt($value, [ + 'keyId' => $key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_INDEXED, + 'queryType' => ClientEncryption::QUERY_TYPE_EQUALITY, + 'contentionFactor' => 0, + ]); + + $results = $collection->find(['encryptedIndexed' => $findPayload])->toArray(); + + $test->assertCount(1, $results); + $test->assertSame($value, $results[0]['encryptedIndexed']); + }, + ]; + + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-2-can-insert-encrypted-indexed-and-find-with-non-zero-contention + yield 'Case 2: can insert encrypted indexed and find with non-zero contention' => [ + static function (self $test, ClientEncryption $clientEncryption, Client $encryptedClient, Client $keyVaultClient, Binary $key1Id): void { + $value = 'encrypted indexed value'; + + $collection = $encryptedClient->selectCollection('db', 'explicit_encryption'); + + for ($i = 0; $i < 10; $i++) { + $insertPayload = $clientEncryption->encrypt($value, [ + 'keyId' => $key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_INDEXED, + 'contentionFactor' => 10, + ]); + + $collection->insertOne(['encryptedIndexed' => $insertPayload]); + } + + $findPayload = $clientEncryption->encrypt($value, [ + 'keyId' => $key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_INDEXED, + 'queryType' => ClientEncryption::QUERY_TYPE_EQUALITY, + 'contentionFactor' => 0, + ]); + + $results = $collection->find(['encryptedIndexed' => $findPayload])->toArray(); + + $test->assertLessThan(10, count($results)); + + foreach ($results as $result) { + $test->assertSame($value, $result['encryptedIndexed']); + } + + $findPayload2 = $clientEncryption->encrypt($value, [ + 'keyId' => $key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_INDEXED, + 'queryType' => ClientEncryption::QUERY_TYPE_EQUALITY, + 'contentionFactor' => 10, + ]); + + $results = $collection->find(['encryptedIndexed' => $findPayload2])->toArray(); + + $test->assertCount(10, $results); + + foreach ($results as $result) { + $test->assertSame($value, $result['encryptedIndexed']); + } + }, + ]; + + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-3-can-insert-encrypted-unindexed + yield 'Case 3: can insert encrypted unindexed' => [ + static function (self $test, ClientEncryption $clientEncryption, Client $encryptedClient, Client $keyVaultClient, Binary $key1Id): void { + $value = 'encrypted unindexed value'; + + $insertPayload = $clientEncryption->encrypt($value, [ + 'keyId' => $key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_UNINDEXED, + ]); + + $collection = $encryptedClient->selectCollection('db', 'explicit_encryption'); + $collection->insertOne(['_id' => 1, 'encryptedUnindexed' => $insertPayload]); + + $results = $collection->find(['_id' => 1])->toArray(); + + $test->assertCount(1, $results); + $test->assertSame($value, $results[0]['encryptedUnindexed']); + }, + ]; + + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-4-can-roundtrip-encrypted-indexed + yield 'Case 4: can roundtrip encrypted indexed' => [ + static function (self $test, ClientEncryption $clientEncryption, Client $encryptedClient, Client $keyVaultClient, Binary $key1Id): void { + $value = 'encrypted indexed value'; + + $payload = $clientEncryption->encrypt($value, [ + 'keyId' => $key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_INDEXED, + 'contentionFactor' => 0, + ]); + + $test->assertSame($value, $clientEncryption->decrypt($payload)); + }, + ]; + + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-5-can-roundtrip-encrypted-unindexed + yield 'Case 5: can roundtrip encrypted unindexed' => [ + static function (self $test, ClientEncryption $clientEncryption, Client $encryptedClient, Client $keyVaultClient, Binary $key1Id): void { + $value = 'encrypted unindexed value'; + + $payload = $clientEncryption->encrypt($value, [ + 'keyId' => $key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_UNINDEXED, + ]); + + $test->assertSame($value, $clientEncryption->decrypt($payload)); + }, + ]; + } + + /** + * Prose test 13: Unique Index on keyAltNames + * + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#unique-index-on-keyaltnames + * @dataProvider provideUniqueIndexOnKeyAltNamesTests + */ + public function testUniqueIndexOnKeyAltNames(Closure $test): void + { + // Test setup + $client = static::createTestClient(); + + // Ensure that the key vault is dropped with a majority write concern + self::insertKeyVaultData($client, []); + + $client->selectCollection('keyvault', 'datakeys')->createIndex( + ['keyAltNames' => 1], + [ + 'unique' => true, + 'partialFilterExpression' => ['keyAltNames' => ['$exists' => true]], + 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), + ] + ); + + $clientEncryption = new ClientEncryption([ + 'keyVaultClient' => $client->getManager(), + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + ]); + + $clientEncryption->createDataKey('local', ['keyAltNames' => ['def']]); + + $test($this, $client, $clientEncryption); + } + + public static function provideUniqueIndexOnKeyAltNamesTests() + { + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-1-createdatakey + yield 'Case 1: createDataKey()' => [ + static function (self $test, Client $client, ClientEncryption $clientEncryption): void { + $clientEncryption->createDataKey('local', ['keyAltNames' => ['abc']]); + + try { + $clientEncryption->createDataKey('local', ['keyAltNames' => ['abc']]); + $test->fail('Expected exception to be thrown'); + } catch (ServerException $e) { + $test->assertSame(11000 /* DuplicateKey */, $e->getCode()); + } + + try { + $clientEncryption->createDataKey('local', ['keyAltNames' => ['def']]); + $test->fail('Expected exception to be thrown'); + } catch (ServerException $e) { + $test->assertSame(11000 /* DuplicateKey */, $e->getCode()); + } + }, + ]; + + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-2-addkeyaltname + yield 'Case 2: addKeyAltName()' => [ + static function (self $test, Client $client, ClientEncryption $clientEncryption): void { + $keyId = $clientEncryption->createDataKey('local'); + + $keyBeforeUpdate = $clientEncryption->addKeyAltName($keyId, 'abc'); + $test->assertObjectNotHasAttribute('keyAltNames', $keyBeforeUpdate); + + $keyBeforeUpdate = $clientEncryption->addKeyAltName($keyId, 'abc'); + $test->assertObjectHasAttribute('keyAltNames', $keyBeforeUpdate); + $test->assertIsArray($keyBeforeUpdate->keyAltNames); + $test->assertContains('abc', $keyBeforeUpdate->keyAltNames); + + try { + $clientEncryption->addKeyAltName($keyId, 'def'); + $test->fail('Expected exception to be thrown'); + } catch (ServerException $e) { + $test->assertSame(11000 /* DuplicateKey */, $e->getCode()); + } + + $originalKeyId = $clientEncryption->getKeyByAltName('def')->_id; + + $originalKeyBeforeUpdate = $clientEncryption->addKeyAltName($originalKeyId, 'def'); + $test->assertObjectHasAttribute('keyAltNames', $originalKeyBeforeUpdate); + $test->assertIsArray($originalKeyBeforeUpdate->keyAltNames); + $test->assertContains('def', $originalKeyBeforeUpdate->keyAltNames); + }, + ]; + } + + /** + * Prose test 14: Decryption Events + * + * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#decryption-events + * @dataProvider provideDecryptionEventsTests + */ + public function testDecryptionEvents(Closure $test): void + { + // Test setup + $setupClient = static::createTestClient(); + $setupClient->selectCollection('db', 'decryption_events')->drop(); + + // Ensure that the key vault is dropped with a majority write concern + self::insertKeyVaultData($setupClient, []); + + $clientEncryption = new ClientEncryption([ + 'keyVaultClient' => $setupClient->getManager(), + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + ]); + + $keyId = $clientEncryption->createDataKey('local'); + + $cipherText = $clientEncryption->encrypt('hello', [ + 'keyId' => $keyId, + 'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, + ]); + + // Flip the last byte in the encrypted string + $malformedCipherText = new Binary(substr($cipherText->getData(), 0, -1) . ~$cipherText->getData()[-1], Binary::TYPE_ENCRYPTED); + + $autoEncryptionOpts = [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + ]; + + $encryptedClient = static::createTestClient(null, ['retryReads' => false], ['autoEncryption' => $autoEncryptionOpts]); + + $subscriber = new class implements CommandSubscriber { + public $lastAggregateReply; + public $lastAggregateError; + + public function commandStarted(CommandStartedEvent $event): void + { + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + if ($event->getCommandName() === 'aggregate') { + $this->lastAggregateReply = $event->getReply(); + } + } + + public function commandFailed(CommandFailedEvent $event): void + { + if ($event->getCommandName() === 'aggregate') { + $this->lastAggregateError = $event->getError(); + } + } + }; + + $encryptedClient->getManager()->addSubscriber($subscriber); + + $test($this, $setupClient, $clientEncryption, $encryptedClient, $subscriber, $cipherText, $malformedCipherText); + + $encryptedClient->getManager()->removeSubscriber($subscriber); + } + + public static function provideDecryptionEventsTests() + { + // See: https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#case-1-command-error + yield 'Case 1: Command Error' => [ + static function (self $test, Client $setupClient, ClientEncryption $clientEncryption, Client $encryptedClient, CommandSubscriber $subscriber, Binary $cipherText, Binary $malformedCipherText): void { + $setupClient->selectDatabase('admin')->command([ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'errorCode' => 123, + 'failCommands' => ['aggregate'], + ], + ]); + + try { + $encryptedClient->selectCollection('db', 'decryption_events')->aggregate([]); + $test->fail('Expected exception to be thrown'); + } catch (CommandException $e) { + $test->assertSame(123, $e->getCode()); + } + + $test->assertNotNull($subscriber->lastAggregateError); + }, + ]; + + // See: https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#case-2-network-error + yield 'Case 2: Network Error' => [ + static function (self $test, Client $setupClient, ClientEncryption $clientEncryption, Client $encryptedClient, CommandSubscriber $subscriber, Binary $cipherText, Binary $malformedCipherText): void { + $setupClient->selectDatabase('admin')->command([ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'closeConnection' => true, + 'failCommands' => ['aggregate'], + ], + ]); + + try { + $encryptedClient->selectCollection('db', 'decryption_events')->aggregate([]); + $test->fail('Expected exception to be thrown'); + } catch (ConnectionTimeoutException $e) { + $test->addToAssertionCount(1); + } + + $test->assertNotNull($subscriber->lastAggregateError); + }, + ]; + + // See: https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#case-3-decrypt-error + yield 'Case 3: Decrypt Error' => [ + static function (self $test, Client $setupClient, ClientEncryption $clientEncryption, Client $encryptedClient, CommandSubscriber $subscriber, Binary $cipherText, Binary $malformedCipherText): void { + $collection = $encryptedClient->selectCollection('db', 'decryption_events'); + + $collection->insertOne(['encrypted' => $malformedCipherText]); + + try { + $collection->aggregate([]); + $test->fail('Expected exception to be thrown'); + } catch (EncryptionException $e) { + $test->assertStringContainsString('HMAC validation failure', $e->getMessage()); + } + + $test->assertNotNull($subscriber->lastAggregateReply); + $test->assertEquals($malformedCipherText, $subscriber->lastAggregateReply->cursor->firstBatch[0]->encrypted ?? null); + }, + ]; + + // See: https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#case-4-decrypt-success + yield 'Case 4: Decrypt Success' => [ + static function (self $test, Client $setupClient, ClientEncryption $clientEncryption, Client $encryptedClient, CommandSubscriber $subscriber, Binary $cipherText, Binary $malformedCipherText): void { + $collection = $encryptedClient->selectCollection('db', 'decryption_events'); + + $collection->insertOne(['encrypted' => $cipherText]); + $collection->aggregate([]); + + $test->assertNotNull($subscriber->lastAggregateReply); + $test->assertEquals($cipherText, $subscriber->lastAggregateReply->cursor->firstBatch[0]->encrypted ?? null); + }, + ]; + } + + /** + * Prose test 15: On-demand AWS Credentials + * + * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#on-demand-aws-credentials + * @group csfle-without-aws-creds + * @testWith [true] + * [false] + */ + public function testOnDemandAwsCredentials(bool $shouldSucceed): void + { + $hasCredentials = (getenv('AWS_ACCESS_KEY_ID') && getenv('AWS_SECRET_ACCESS_KEY')); + + if ($hasCredentials !== $shouldSucceed) { + Assert::markTestSkipped(sprintf('AWS credentials %s available', $hasCredentials ? 'are' : 'are not')); + } + + $keyVaultClient = static::createTestClient(); + + $clientEncryption = new ClientEncryption([ + 'keyVaultClient' => $keyVaultClient->getManager(), + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['aws' => (object) []], + ]); + + $dataKeyOpts = [ + 'masterKey' => [ + 'region' => 'us-east-1', + 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0', + ], + ]; + + if (! $shouldSucceed) { + $this->expectException(AuthenticationException::class); + } + + $dataKeyId = $clientEncryption->createDataKey('aws', $dataKeyOpts); + + if ($shouldSucceed) { + $this->assertInstanceOf(Binary::class, $dataKeyId); + $this->assertSame(Binary::TYPE_UUID, $dataKeyId->getType()); + } + } + + /** + * Prose test 16: RewrapManyDataKey + * + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#rewrap + * @dataProvider provideRewrapManyDataKeySrcAndDstProviders + */ + public function testRewrapManyDataKey(string $srcProvider, string $dstProvider): void + { + $providerMasterKeys = [ + 'aws' => ['region' => 'us-east-1', 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0'], + 'azure' => ['keyVaultEndpoint' => 'key-vault-csfle.vault.azure.net', 'keyName' => 'key-name-csfle'], + 'gcp' => ['projectId' => 'devprod-drivers', 'location' => 'global', 'keyRing' => 'key-ring-csfle', 'keyName' => 'key-name-csfle'], + 'kmip' => [], + ]; + + // Test setup + $client = static::createTestClient(); + + // Ensure that the key vault is dropped with a majority write concern + self::insertKeyVaultData($client, []); + + $clientEncryptionOpts = [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'aws' => Context::getAWSCredentials(), + 'azure' => Context::getAzureCredentials(), + 'gcp' => Context::getGCPCredentials(), + 'kmip' => ['endpoint' => Context::getKmipEndpoint()], + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + ], + 'tlsOptions' => [ + 'kmip' => Context::getKmsTlsOptions(), + ], + ]; + + $clientEncryption1 = $client->createClientEncryption($clientEncryptionOpts); + + $createDataKeyOpts = []; + + if (isset($providerMasterKeys[$srcProvider])) { + $createDataKeyOpts['masterKey'] = $providerMasterKeys[$srcProvider]; + } + + $keyId = $clientEncryption1->createDataKey($srcProvider, $createDataKeyOpts); + + $ciphertext = $clientEncryption1->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]); + + $clientEncryption2 = $client->createClientEncryption($clientEncryptionOpts); + + $rewrapManyDataKeyOpts = ['provider' => $dstProvider]; + + if (isset($providerMasterKeys[$dstProvider])) { + $rewrapManyDataKeyOpts['masterKey'] = $providerMasterKeys[$dstProvider]; + } + + $result = $clientEncryption2->rewrapManyDataKey([], $rewrapManyDataKeyOpts); + + $this->assertObjectHasAttribute('bulkWriteResult', $result); + $this->assertIsObject($result->bulkWriteResult); + // libmongoc uses different field names for its BulkWriteResult + $this->assertObjectHasAttribute('nModified', $result->bulkWriteResult); + $this->assertSame(1, $result->bulkWriteResult->nModified); + + $this->assertSame('test', $clientEncryption1->decrypt($ciphertext)); + $this->assertSame('test', $clientEncryption2->decrypt($ciphertext)); + } + + public static function provideRewrapManyDataKeySrcAndDstProviders() + { + $providers = ['aws', 'azure', 'gcp', 'kmip', 'local']; + + foreach ($providers as $srcProvider) { + foreach ($providers as $dstProvider) { + yield [$srcProvider, $dstProvider]; + } + } + } + + private function createTestCollection(?stdClass $encryptedFields = null, ?stdClass $jsonSchema = null): void + { + $context = $this->getContext(); + $options = $context->defaultWriteOptions; + + if (! empty($encryptedFields)) { + $options['encryptedFields'] = $encryptedFields; + } + + if (! empty($jsonSchema)) { + $options['validator'] = ['$jsonSchema' => $jsonSchema]; + } + + $context->getDatabase()->createCollection($context->collectionName, $options); + } + + private function encryptCorpusValue(string $fieldName, stdClass $data, ClientEncryption $clientEncryption) + { + $encryptionOptions = [ + 'algorithm' => $data->algo === 'rand' ? ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_RANDOM : ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, + ]; + + switch ($data->kms) { + case 'local': + $keyId = 'LOCALAAAAAAAAAAAAAAAAA=='; + $keyAltName = 'local'; + break; + case 'aws': + $keyId = 'AWSAAAAAAAAAAAAAAAAAAA=='; + $keyAltName = 'aws'; + break; + case 'azure': + $keyId = 'AZUREAAAAAAAAAAAAAAAAA=='; + $keyAltName = 'azure'; + break; + case 'gcp': + $keyId = 'GCPAAAAAAAAAAAAAAAAAAA=='; + $keyAltName = 'gcp'; + break; + case 'kmip': + $keyId = 'KMIPAAAAAAAAAAAAAAAAAA=='; + $keyAltName = 'kmip'; + break; + + default: + throw new UnexpectedValueException(sprintf('Unexpected KMS "%s"', $data->kms)); + } + + switch ($data->identifier) { + case 'id': + $encryptionOptions['keyId'] = new Binary(base64_decode($keyId), 4); + break; + + case 'altname': + $encryptionOptions['keyAltName'] = $keyAltName; + break; + + default: + throw new UnexpectedValueException(sprintf('Unexpected value "%s" for identifier', $data->identifier)); + } + + if ($data->allowed) { + try { + /* Note: workaround issue where mongocryptd refuses to encrypt + * 32-bit integers if schemaMap defines a "long" BSON type. */ + $value = $data->type === 'long' && ! $data->value instanceof Int64 + ? new Int64($data->value) + : $data->value; + + $encrypted = $clientEncryption->encrypt($value, $encryptionOptions); + } catch (EncryptionException $e) { + $this->fail('Could not encrypt value for field ' . $fieldName . ': ' . $e->getMessage()); + } + + $this->assertEquals($data->value, $clientEncryption->decrypt($encrypted)); + + return $encrypted; + } + + try { + $clientEncryption->encrypt($data->value, $encryptionOptions); + $this->fail('Expected exception to be thrown'); + } catch (RuntimeException $e) { + } + + return $data->value; + } + + private static function getEnv(string $name): string + { + $value = getenv($name); + + if ($value === false) { + Assert::markTestSkipped(sprintf('Environment variable "%s" is not defined', $name)); + } + + return $value; + } + + private static function insertKeyVaultData(Client $client, ?array $keyVaultData = null): void + { + $collection = $client->selectCollection('keyvault', 'datakeys', ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]); + $collection->drop(); + + if (empty($keyVaultData)) { + return; + } + + $collection->insertMany($keyVaultData); + } + + private function prepareCorpusData(string $fieldName, stdClass $data, ClientEncryption $clientEncryption) + { + if ($data->method === 'auto') { + /* Note: workaround issue where mongocryptd refuses to encrypt + * 32-bit integers if schemaMap defines a "long" BSON type. */ + if ($data->type === 'long' && ! $data->value instanceof Int64) { + $data->value = new Int64($data->value); + } + + return $data; + } + + $returnData = clone $data; + $returnData->value = $this->encryptCorpusValue($fieldName, $data, $clientEncryption); + + return $data->allowed ? $returnData : $data; + } + + private static function isCryptSharedLibAvailable(): bool + { + $cryptSharedLibPath = getenv('CRYPT_SHARED_LIB_PATH'); + + if ($cryptSharedLibPath === false) { + return false; + } + + return is_readable($cryptSharedLibPath); + } + + private static function isMongocryptdAvailable(): bool + { + $paths = explode(PATH_SEPARATOR, getenv("PATH")); + + foreach ($paths as $path) { + if (is_executable($path . DIRECTORY_SEPARATOR . 'mongocryptd')) { + return true; + } + } + + return false; } } diff --git a/tests/SpecTests/CommandExpectations.php b/tests/SpecTests/CommandExpectations.php index 6e203f931..86492a3ed 100644 --- a/tests/SpecTests/CommandExpectations.php +++ b/tests/SpecTests/CommandExpectations.php @@ -4,16 +4,16 @@ use ArrayIterator; use LogicException; +use MongoDB\Client; use MongoDB\Driver\Monitoring\CommandFailedEvent; use MongoDB\Driver\Monitoring\CommandStartedEvent; use MongoDB\Driver\Monitoring\CommandSubscriber; use MongoDB\Driver\Monitoring\CommandSucceededEvent; use MultipleIterator; + use function count; use function in_array; use function key; -use function MongoDB\Driver\Monitoring\addSubscriber; -use function MongoDB\Driver\Monitoring\removeSubscriber; /** * Spec test CommandStartedEvent expectations. @@ -38,22 +38,33 @@ class CommandExpectations implements CommandSubscriber /** @var boolean */ private $ignoreExtraEvents = false; + /** @var boolean */ + private $ignoreKeyVaultListCollections = false; + /** @var string[] */ private $ignoredCommandNames = []; - private function __construct(array $events) + /** @var Client */ + private $observedClient; + + private function __construct(Client $observedClient, array $events) { + $this->observedClient = $observedClient; + foreach ($events as $event) { - switch (key($event)) { + switch (key((array) $event)) { case 'command_failed_event': + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps $this->expectedEvents[] = [$event->command_failed_event, CommandFailedEvent::class]; break; case 'command_started_event': + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps $this->expectedEvents[] = [$event->command_started_event, CommandStartedEvent::class]; break; case 'command_succeeded_event': + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps $this->expectedEvents[] = [$event->command_succeeded_event, CommandSucceededEvent::class]; break; @@ -63,23 +74,20 @@ private function __construct(array $events) } } - public static function fromChangeStreams(array $expectedEvents) + public static function fromClientSideEncryption(Client $client, array $expectedEvents) { - $o = new self($expectedEvents); + $o = new self($client, $expectedEvents); $o->ignoreCommandFailed = true; $o->ignoreCommandSucceeded = true; - /* Change Streams spec tests do not include getMore commands in the - * list of expected events, so ignore any observed events beyond the - * number that are expected. */ - $o->ignoreExtraEvents = true; + $o->ignoreKeyVaultListCollections = true; return $o; } - public static function fromClientSideEncryption(array $expectedEvents) + public static function fromCrud(Client $client, array $expectedEvents) { - $o = new self($expectedEvents); + $o = new self($client, $expectedEvents); $o->ignoreCommandFailed = true; $o->ignoreCommandSucceeded = true; @@ -87,14 +95,9 @@ public static function fromClientSideEncryption(array $expectedEvents) return $o; } - public static function fromCommandMonitoring(array $expectedEvents) + public static function fromReadWriteConcern(Client $client, array $expectedEvents) { - return new self($expectedEvents); - } - - public static function fromCrud(array $expectedEvents) - { - $o = new self($expectedEvents); + $o = new self($client, $expectedEvents); $o->ignoreCommandFailed = true; $o->ignoreCommandSucceeded = true; @@ -102,19 +105,9 @@ public static function fromCrud(array $expectedEvents) return $o; } - public static function fromReadWriteConcern(array $expectedEvents) + public static function fromRetryableReads(Client $client, array $expectedEvents) { - $o = new self($expectedEvents); - - $o->ignoreCommandFailed = true; - $o->ignoreCommandSucceeded = true; - - return $o; - } - - public static function fromRetryableReads(array $expectedEvents) - { - $o = new self($expectedEvents); + $o = new self($client, $expectedEvents); $o->ignoreCommandFailed = true; $o->ignoreCommandSucceeded = true; @@ -127,9 +120,9 @@ public static function fromRetryableReads(array $expectedEvents) return $o; } - public static function fromTransactions(array $expectedEvents) + public static function fromTransactions(Client $client, array $expectedEvents) { - $o = new self($expectedEvents); + $o = new self($client, $expectedEvents); $o->ignoreCommandFailed = true; $o->ignoreCommandSucceeded = true; @@ -148,9 +141,9 @@ public static function fromTransactions(array $expectedEvents) /** * Not used. * - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php + * @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php */ - public function commandFailed(CommandFailedEvent $event) + public function commandFailed(CommandFailedEvent $event): void { if ($this->ignoreCommandFailed || $this->isEventIgnored($event)) { return; @@ -162,9 +155,9 @@ public function commandFailed(CommandFailedEvent $event) /** * Tracks outgoing commands for spec test APM assertions. * - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php + * @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php */ - public function commandStarted(CommandStartedEvent $event) + public function commandStarted(CommandStartedEvent $event): void { if ($this->ignoreCommandStarted || $this->isEventIgnored($event)) { return; @@ -176,9 +169,9 @@ public function commandStarted(CommandStartedEvent $event) /** * Not used. * - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php + * @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php */ - public function commandSucceeded(CommandSucceededEvent $event) + public function commandSucceeded(CommandSucceededEvent $event): void { if ($this->ignoreCommandSucceeded || $this->isEventIgnored($event)) { return; @@ -190,26 +183,23 @@ public function commandSucceeded(CommandSucceededEvent $event) /** * Start command monitoring. */ - public function startMonitoring() + public function startMonitoring(): void { - addSubscriber($this); + $this->observedClient->getManager()->addSubscriber($this); } /** * Stop command monitoring. */ - public function stopMonitoring() + public function stopMonitoring(): void { - removeSubscriber($this); + $this->observedClient->getManager()->removeSubscriber($this); } /** * Assert that the command expectations match the monitored events. - * - * @param FunctionalTestCase $test Test instance - * @param Context $context Execution context */ - public function assert(FunctionalTestCase $test, Context $context) + public function assert(FunctionalTestCase $test, Context $context): void { $test->assertCount(count($this->expectedEvents), $this->actualEvents); @@ -218,11 +208,12 @@ public function assert(FunctionalTestCase $test, Context $context) $mi->attachIterator(new ArrayIterator($this->actualEvents)); foreach ($mi as $events) { - list($expectedEventAndClass, $actualEvent) = $events; - list($expectedEvent, $expectedClass) = $expectedEventAndClass; + [$expectedEventAndClass, $actualEvent] = $events; + [$expectedEvent, $expectedClass] = $expectedEventAndClass; $test->assertInstanceOf($expectedClass, $actualEvent); + // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps if (isset($expectedEvent->command_name)) { $test->assertSame($expectedEvent->command_name, $actualEvent->getCommandName()); } @@ -231,6 +222,8 @@ public function assert(FunctionalTestCase $test, Context $context) $test->assertSame($expectedEvent->database_name, $actualEvent->getDatabaseName()); } + // phpcs:enable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + if (isset($expectedEvent->command)) { $test->assertInstanceOf(CommandStartedEvent::class, $actualEvent); $expectedCommand = $expectedEvent->command; @@ -247,7 +240,24 @@ public function assert(FunctionalTestCase $test, Context $context) private function isEventIgnored($event) { - return ($this->ignoreExtraEvents && count($this->actualEvents) === count($this->expectedEvents)) - || in_array($event->getCommandName(), $this->ignoredCommandNames); + if ($this->ignoreExtraEvents && count($this->actualEvents) === count($this->expectedEvents)) { + return true; + } + + if (in_array($event->getCommandName(), $this->ignoredCommandNames)) { + return true; + } + + /* Note: libmongoc does not use a separate MongoClient to query for + * CSFLE metadata (DRIVERS-1459). Since the tests do not expect this + * command, we must ignore it. */ + if ( + $this->ignoreKeyVaultListCollections && $event instanceof CommandStartedEvent && + $event->getCommandName() === 'listCollections' && $event->getDatabaseName() === 'keyvault' + ) { + return true; + } + + return false; } } diff --git a/tests/SpecTests/CommandMonitoringSpecTest.php b/tests/SpecTests/CommandMonitoringSpecTest.php deleted file mode 100644 index 0f9bf9075..000000000 --- a/tests/SpecTests/CommandMonitoringSpecTest.php +++ /dev/null @@ -1,223 +0,0 @@ -getMore) && $expected->getMore === 42) { - static::assertObjectHasAttribute('getMore', $actual); - static::assertThat($actual->getMore, static::logicalOr( - static::isInstanceOf(Int64::class), - static::isType('integer') - )); - unset($expected->getMore); - } - - if (isset($expected->killCursors) && isset($expected->cursors) && is_array($expected->cursors)) { - static::assertObjectHasAttribute('cursors', $actual); - static::assertIsArray($actual->cursors); - - foreach ($expected->cursors as $i => $cursorId) { - static::assertArrayHasKey($i, $actual->cursors); - - if ($cursorId === 42) { - static::assertThat($actual->cursors[$i], static::logicalOr( - static::isInstanceOf(Int64::class), - static::isType('integer') - )); - } - } - - unset($expected->cursors); - } - - static::assertDocumentsMatch($expected, $actual); - } - - /** - * Assert that the expected and actual command reply documents match. - * - * Note: this method may modify the $expectedReply object. - * - * @param stdClass $expected Expected command reply document - * @param stdClass $actual Actual command reply document - */ - public static function assertCommandReplyMatches(stdClass $expected, stdClass $actual) - { - if (isset($expected->cursor->id) && $expected->cursor->id === 42) { - static::assertObjectHasAttribute('cursor', $actual); - static::assertIsObject($actual->cursor); - static::assertObjectHasAttribute('id', $actual->cursor); - static::assertThat($actual->cursor->id, static::logicalOr( - static::isInstanceOf(Int64::class), - static::isType('integer') - )); - unset($expected->cursor->id); - } - - if (isset($expected->cursorsUnknown) && is_array($expected->cursorsUnknown)) { - static::assertObjectHasAttribute('cursorsUnknown', $actual); - static::assertIsArray($actual->cursorsUnknown); - - foreach ($expected->cursorsUnknown as $i => $cursorId) { - static::assertArrayHasKey($i, $actual->cursorsUnknown); - - if ($cursorId === 42) { - static::assertThat($actual->cursorsUnknown[$i], static::logicalOr( - static::isInstanceOf(Int64::class), - static::isType('integer') - )); - } - } - - unset($expected->cursorsUnknown); - } - - if (isset($expected->ok) && is_numeric($expected->ok)) { - static::assertObjectHasAttribute('ok', $actual); - static::assertIsNumeric($actual->ok); - static::assertEquals($expected->ok, $actual->ok); - unset($expected->ok); - } - - if (isset($expected->writeErrors) && is_array($expected->writeErrors)) { - static::assertObjectHasAttribute('writeErrors', $actual); - static::assertIsArray($actual->writeErrors); - - foreach ($expected->writeErrors as $i => $expectedWriteError) { - static::assertArrayHasKey($i, $actual->writeErrors); - $actualWriteError = $actual->writeErrors[$i]; - - if (isset($expectedWriteError->code) && $expectedWriteError->code === 42) { - static::assertObjectHasAttribute('code', $actualWriteError); - static::assertThat($actualWriteError->code, static::logicalOr( - static::isInstanceOf(Int64::class), - static::isType('integer') - )); - unset($expected->writeErrors[$i]->code); - } - - if (isset($expectedWriteError->errmsg) && $expectedWriteError->errmsg === '') { - static::assertObjectHasAttribute('errmsg', $actualWriteError); - static::assertIsString($actualWriteError->errmsg); - static::assertNotEmpty($actualWriteError->errmsg); - unset($expected->writeErrors[$i]->errmsg); - } - } - } - - static::assertDocumentsMatch($expected, $actual); - } - - /** - * Execute an individual test case from the specification. - * - * @dataProvider provideTests - * @param stdClass $test Individual "tests[]" document - * @param array $data Top-level "data" array to initialize collection - * @param string $databaseName Name of database under test - * @param string $collectionName Name of collection under test - */ - public function testCommandMonitoring(stdClass $test, array $data, $databaseName = null, $collectionName = null) - { - $this->checkServerRequirements($this->createRunOn($test)); - - $databaseName = $databaseName ?? $this->getDatabaseName(); - $collectionName = $collectionName ?? $this->getCollectionName(); - - $context = Context::fromCommandMonitoring($test, $databaseName, $collectionName); - $this->setContext($context); - - $this->dropTestAndOutcomeCollections(); - $this->insertDataFixtures($data); - - if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromCommandMonitoring($test->expectations); - $commandExpectations->startMonitoring(); - } - - Operation::fromCommandMonitoring($test->operation)->assert($this, $context); - - if (isset($commandExpectations)) { - $commandExpectations->stopMonitoring(); - $commandExpectations->assert($this, $context); - } - } - - public function provideTests() - { - $testArgs = []; - - foreach (glob(__DIR__ . '/command-monitoring/*.json') as $filename) { - $json = $this->decodeJson(file_get_contents($filename)); - $group = basename($filename, '.json'); - $data = $json->data ?? []; - $databaseName = $json->database_name ?? null; - $collectionName = $json->collection_name ?? null; - - foreach ($json->tests as $test) { - $name = $group . ': ' . $test->description; - $testArgs[$name] = [$test, $data, $databaseName, $collectionName]; - } - } - - return $testArgs; - } - - /** - * Convert the server and topology requirements to a standard "runOn" array - * used by other specifications. - * - * @param stdClass $test - * @return array - */ - private function createRunOn(stdClass $test) - { - $req = new stdClass(); - - $topologies = [ - self::TOPOLOGY_SINGLE, - self::TOPOLOGY_REPLICASET, - self::TOPOLOGY_SHARDED, - ]; - - /* Append ".99" as patch version, since command monitoring tests expect - * the minor version to be an inclusive upper bound. */ - if (isset($test->ignore_if_server_version_greater_than)) { - $req->maxServerVersion = $test->ignore_if_server_version_greater_than . '.99'; - } - - if (isset($test->ignore_if_server_version_less_than)) { - $req->minServerVersion = $test->ignore_if_server_version_less_than; - } - - if (isset($test->ignore_if_topology_type)) { - $req->topology = array_diff($topologies, $test->ignore_if_topology_type); - } - - return [$req]; - } -} diff --git a/tests/SpecTests/Context.php b/tests/SpecTests/Context.php index dde7304d7..326061639 100644 --- a/tests/SpecTests/Context.php +++ b/tests/SpecTests/Context.php @@ -8,14 +8,15 @@ use MongoDB\Driver\ReadPreference; use MongoDB\Driver\Session; use MongoDB\Driver\WriteConcern; -use PHPUnit\Framework\SkippedTestError; +use PHPUnit\Framework\Assert; use stdClass; + use function array_diff_key; use function array_keys; use function getenv; use function implode; -use function mt_rand; -use function uniqid; +use function PHPUnit\Framework\assertLessThanOrEqual; +use function sprintf; /** * Execution context for spec tests. @@ -31,7 +32,7 @@ final class Context /** @var Client|null */ private $client; - /** @var string */ + /** @var string|null */ public $collectionName; /** @var string */ @@ -43,7 +44,7 @@ final class Context /** @var array */ public $outcomeReadOptions = []; - /** @var string */ + /** @var string|null */ public $outcomeCollectionName; /** @var Session|null */ @@ -58,44 +59,21 @@ final class Context /** @var object */ public $session1Lsid; + /** @var bool */ + public $useEncryptedClientIfConfigured = false; + + /** @var Client */ + private $internalClient; + /** @var Client|null */ private $encryptedClient; - /** @var bool */ - private $useEncryptedClient = false; - - /** - * @param string $databaseName - * @param string $collectionName - */ - private function __construct($databaseName, $collectionName) + private function __construct(string $databaseName, ?string $collectionName) { $this->databaseName = $databaseName; $this->collectionName = $collectionName; $this->outcomeCollectionName = $collectionName; - } - - public function disableEncryption() - { - $this->useEncryptedClient = false; - } - - public function enableEncryption() - { - if (! $this->encryptedClient instanceof Client) { - throw new LogicException('Cannot enable encryption without autoEncryption options'); - } - - $this->useEncryptedClient = true; - } - - public static function fromChangeStreams(stdClass $test, $databaseName, $collectionName) - { - $o = new self($databaseName, $collectionName); - - $o->client = new Client(FunctionalTestCase::getUri()); - - return $o; + $this->internalClient = FunctionalTestCase::createTestClient(); } public static function fromClientSideEncryption(stdClass $test, $databaseName, $collectionName) @@ -104,44 +82,71 @@ public static function fromClientSideEncryption(stdClass $test, $databaseName, $ $clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : []; - /* mongocryptd caches collection information, which causes test failures - * if we reuse the client. Thus, we add a random value to ensure we're - * creating a new client for each test. */ - $driverOptions = ['random' => uniqid()]; - $autoEncryptionOptions = []; if (isset($clientOptions['autoEncryptOpts'])) { $autoEncryptionOptions = (array) $clientOptions['autoEncryptOpts'] + ['keyVaultNamespace' => 'keyvault.datakeys']; unset($clientOptions['autoEncryptOpts']); + // Ensure test doesn't specify conflicting options for AWS + $countAws = (isset($autoEncryptionOptions['kmsProviders']->aws) ? 1 : 0); + $countAws += (isset($autoEncryptionOptions['kmsProviders']->awsTemporary) ? 1 : 0); + $countAws += (isset($autoEncryptionOptions['kmsProviders']->awsTemporaryNoSessionToken) ? 1 : 0); + assertLessThanOrEqual(1, $countAws, 'aws, awsTemporary, and awsTemporaryNoSessionToken are mutually exclusive'); + if (isset($autoEncryptionOptions['kmsProviders']->aws)) { $autoEncryptionOptions['kmsProviders']->aws = self::getAWSCredentials(); } + + if (isset($autoEncryptionOptions['kmsProviders']->awsTemporary)) { + unset($autoEncryptionOptions['kmsProviders']->awsTemporary); + $autoEncryptionOptions['kmsProviders']->aws = self::getAWSTempCredentials(true); + } + + if (isset($autoEncryptionOptions['kmsProviders']->awsTemporaryNoSessionToken)) { + unset($autoEncryptionOptions['kmsProviders']->awsTemporaryNoSessionToken); + $autoEncryptionOptions['kmsProviders']->aws = self::getAWSTempCredentials(false); + } + + if (isset($autoEncryptionOptions['kmsProviders']->azure)) { + $autoEncryptionOptions['kmsProviders']->azure = self::getAzureCredentials(); + } + + if (isset($autoEncryptionOptions['kmsProviders']->gcp)) { + $autoEncryptionOptions['kmsProviders']->gcp = self::getGCPCredentials(); + } + + if (isset($autoEncryptionOptions['kmsProviders']->kmip)) { + $autoEncryptionOptions['kmsProviders']->kmip = ['endpoint' => self::getKmipEndpoint()]; + + if (empty($autoEncryptionOptions['tlsOptions'])) { + $autoEncryptionOptions['tlsOptions'] = new stdClass(); + } + + $autoEncryptionOptions['tlsOptions']->kmip = self::getKmsTlsOptions(); + } + + // Intentionally ignore empty values for CRYPT_SHARED_LIB_PATH + if (getenv('CRYPT_SHARED_LIB_PATH')) { + $autoEncryptionOptions['extraOptions']['cryptSharedLibPath'] = getenv('CRYPT_SHARED_LIB_PATH'); + } } if (isset($test->outcome->collection->name)) { $o->outcomeCollectionName = $test->outcome->collection->name; } - $o->client = new Client(FunctionalTestCase::getUri(), $clientOptions, $driverOptions); + $o->defaultWriteOptions = ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]; + + $o->client = self::createTestClient(null, $clientOptions); if ($autoEncryptionOptions !== []) { - $o->encryptedClient = new Client(FunctionalTestCase::getUri(), $clientOptions, $driverOptions + ['autoEncryption' => $autoEncryptionOptions]); + $o->encryptedClient = self::createTestClient(null, $clientOptions, ['autoEncryption' => $autoEncryptionOptions]); } return $o; } - public static function fromCommandMonitoring(stdClass $test, $databaseName, $collectionName) - { - $o = new self($databaseName, $collectionName); - - $o->client = new Client(FunctionalTestCase::getUri()); - - return $o; - } - public static function fromCrud(stdClass $test, $databaseName, $collectionName) { $o = new self($databaseName, $collectionName); @@ -158,10 +163,10 @@ public static function fromCrud(stdClass $test, $databaseName, $collectionName) $o->outcomeReadOptions = [ 'readConcern' => new ReadConcern('local'), - 'readPreference' => new ReadPreference('primary'), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), ]; - $o->client = new Client(FunctionalTestCase::getUri(), $clientOptions); + $o->client = self::createTestClient(null, $clientOptions); return $o; } @@ -176,7 +181,7 @@ public static function fromReadWriteConcern(stdClass $test, $databaseName, $coll $clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : []; - $o->client = new Client(FunctionalTestCase::getUri(), $clientOptions); + $o->client = self::createTestClient(null, $clientOptions); return $o; } @@ -189,7 +194,7 @@ public static function fromRetryableReads(stdClass $test, $databaseName, $collec $clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : []; - $o->client = new Client(FunctionalTestCase::getUri(), $clientOptions); + $o->client = self::createTestClient(null, $clientOptions); return $o; } @@ -204,7 +209,7 @@ public static function fromRetryableWrites(stdClass $test, $databaseName, $colle $o->outcomeCollectionName = $test->outcome->collection->name; } - $o->client = new Client(FunctionalTestCase::getUri($useMultipleMongoses), $clientOptions); + $o->client = self::createTestClient(FunctionalTestCase::getUri($useMultipleMongoses), $clientOptions); return $o; } @@ -219,17 +224,12 @@ public static function fromTransactions(stdClass $test, $databaseName, $collecti $o->outcomeReadOptions = [ 'readConcern' => new ReadConcern('local'), - 'readPreference' => new ReadPreference('primary'), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), ]; $clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : []; - /* Transaction spec tests expect a new client for each test so that - * txnNumber values are deterministic. Append a random option to avoid - * re-using a previously persisted libmongoc client object. */ - $clientOptions += ['p' => mt_rand()]; - - $o->client = new Client(FunctionalTestCase::getUri($useMultipleMongoses), $clientOptions); + $o->client = self::createTestClient(FunctionalTestCase::getUri($useMultipleMongoses), $clientOptions); $session0Options = isset($test->sessionOptions->session0) ? (array) $test->sessionOptions->session0 : []; $session1Options = isset($test->sessionOptions->session1) ? (array) $test->sessionOptions->session1 : []; @@ -243,29 +243,61 @@ public static function fromTransactions(stdClass $test, $databaseName, $collecti return $o; } - /** - * @return array - * - * @throws SkippedTestError - */ - public static function getAWSCredentials() + public static function getAWSCredentials(): array { - if (! getenv('AWS_ACCESS_KEY_ID') || ! getenv('AWS_SECRET_ACCESS_KEY')) { - throw new SkippedTestError('Please configure AWS credentials to use AWS KMS provider.'); + return [ + 'accessKeyId' => static::getEnv('AWS_ACCESS_KEY_ID'), + 'secretAccessKey' => static::getEnv('AWS_SECRET_ACCESS_KEY'), + ]; + } + + public static function getAWSTempCredentials(bool $withSessionToken): array + { + $awsTempCredentials = [ + 'accessKeyId' => static::getEnv('AWS_TEMP_ACCESS_KEY_ID'), + 'secretAccessKey' => static::getEnv('AWS_TEMP_SECRET_ACCESS_KEY'), + ]; + + if ($withSessionToken) { + $awsTempCredentials['sessionToken'] = static::getEnv('AWS_TEMP_SESSION_TOKEN'); } + return $awsTempCredentials; + } + + public static function getAzureCredentials(): array + { return [ - 'accessKeyId' => getenv('AWS_ACCESS_KEY_ID'), - 'secretAccessKey' => getenv('AWS_SECRET_ACCESS_KEY'), + 'tenantId' => static::getEnv('AZURE_TENANT_ID'), + 'clientId' => static::getEnv('AZURE_CLIENT_ID'), + 'clientSecret' => static::getEnv('AZURE_CLIENT_SECRET'), ]; } - /** - * @return Client - */ - public function getClient() + public static function getKmipEndpoint(): string + { + return static::getEnv('KMIP_ENDPOINT'); + } + + public static function getKmsTlsOptions(): array { - return $this->useEncryptedClient && $this->encryptedClient ? $this->encryptedClient : $this->client; + return [ + 'tlsCAFile' => static::getEnv('KMS_TLS_CA_FILE'), + 'tlsCertificateKeyFile' => static::getEnv('KMS_TLS_CERTIFICATE_KEY_FILE'), + ]; + } + + public static function getGCPCredentials(): array + { + return [ + 'email' => static::getEnv('GCP_EMAIL'), + 'privateKey' => static::getEnv('GCP_PRIVATE_KEY'), + ]; + } + + public function getClient(): Client + { + return $this->useEncryptedClientIfConfigured && $this->encryptedClient ? $this->encryptedClient : $this->client; } public function getCollection(array $collectionOptions = [], array $databaseOptions = []) @@ -288,15 +320,18 @@ public function getGridFSBucket(array $bucketOptions = []) return $this->selectGridFSBucket($this->databaseName, $this->bucketName, $bucketOptions); } + public function getInternalClient(): Client + { + return $this->internalClient; + } + /** * Prepare options readConcern, readPreference, and writeConcern options by * creating value objects. * - * @param array $options - * @return array * @throws LogicException if any option keys are unsupported */ - public function prepareOptions(array $options) + public function prepareOptions(array $options): array { if (isset($options['readConcern']) && ! ($options['readConcern'] instanceof ReadConcern)) { $readConcern = (array) $options['readConcern']; @@ -352,7 +387,7 @@ public function prepareOptions(array $options) * @param array $args Operation arguments * @throws LogicException if the session placeholder is unsupported */ - public function replaceArgumentSessionPlaceholder(array &$args) + public function replaceArgumentSessionPlaceholder(array &$args): void { if (! isset($args['session'])) { return; @@ -380,7 +415,7 @@ public function replaceArgumentSessionPlaceholder(array &$args) * @param stdClass $command Command document * @throws LogicException if the session placeholder is unsupported */ - public function replaceCommandSessionPlaceholder(stdClass $command) + public function replaceCommandSessionPlaceholder(stdClass $command): void { if (! isset($command->lsid)) { return; @@ -420,6 +455,27 @@ public function selectGridFSBucket($databaseName, $bucketName, array $bucketOpti return $this->selectDatabase($databaseName)->selectGridFSBucket($this->prepareGridFSBucketOptions($bucketOptions, $bucketName)); } + private static function createTestClient(?string $uri = null, array $options = [], array $driverOptions = []): Client + { + /* Default to using a dedicated client. This was already necessary for + * CSFLE and Transaction spec tests, but is generally useful for any + * test that observes command monitoring events. */ + $driverOptions += ['disableClientPersistence' => true]; + + return FunctionalTestCase::createTestClient($uri, $options, $driverOptions); + } + + private static function getEnv(string $name): string + { + $value = getenv($name); + + if ($value === false) { + Assert::markTestSkipped(sprintf('Environment variable "%s" is not defined', $name)); + } + + return $value; + } + private function prepareGridFSBucketOptions(array $options, $bucketPrefix) { if ($bucketPrefix !== null) { diff --git a/tests/SpecTests/CrudSpecTest.php b/tests/SpecTests/CrudSpecTest.php deleted file mode 100644 index 8835b92e6..000000000 --- a/tests/SpecTests/CrudSpecTest.php +++ /dev/null @@ -1,156 +0,0 @@ - $value) { - if ($value === null) { - static::assertObjectNotHasAttribute($key, $actual); - unset($expected->{$key}); - } - } - - static::assertDocumentsMatch($expected, $actual); - } - - /** - * Execute an individual test case from the specification. - * - * @dataProvider provideTests - * @param stdClass $test Individual "tests[]" document - * @param array $runOn Top-level "runOn" array with server requirements - * @param array $data Top-level "data" array to initialize collection - * @param string $databaseName Name of database under test - * @param string $collectionName Name of collection under test - */ - public function testCrud(stdClass $test, array $runOn = null, array $data, $databaseName = null, $collectionName = null) - { - if (isset(self::$incompleteTests[$this->dataDescription()])) { - $this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]); - } - - if (isset($runOn)) { - $this->checkServerRequirements($runOn); - } - - if (isset($test->skipReason)) { - $this->markTestSkipped($test->skipReason); - } - - $databaseName = $databaseName ?? $this->getDatabaseName(); - $collectionName = $collectionName ?? $this->getCollectionName(); - - $context = Context::fromCrud($test, $databaseName, $collectionName); - $this->setContext($context); - - $this->dropTestAndOutcomeCollections(); - $this->insertDataFixtures($data); - - if (isset($test->failPoint)) { - $this->configureFailPoint($test->failPoint); - } - - if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromCrud((array) $test->expectations); - $commandExpectations->startMonitoring(); - } - - foreach ($test->operations as $operation) { - Operation::fromCrud($operation)->assert($this, $context); - } - - if (isset($commandExpectations)) { - $commandExpectations->stopMonitoring(); - $commandExpectations->assert($this, $context); - } - - if (isset($test->outcome->collection->data)) { - $this->assertOutcomeCollectionData($test->outcome->collection->data); - } - } - - public function provideTests() - { - $testArgs = []; - - foreach (glob(__DIR__ . '/crud/*.json') as $filename) { - $json = $this->decodeJson(file_get_contents($filename)); - $group = basename($filename, '.json'); - $runOn = $json->runOn ?? null; - $data = $json->data ?? []; - $databaseName = $json->database_name ?? null; - $collectionName = $json->collection_name ?? null; - - foreach ($json->tests as $test) { - $name = $group . ': ' . $test->description; - $testArgs[$name] = [$test, $runOn, $data, $databaseName, $collectionName]; - } - } - - return $testArgs; - } - - /** - * Prose test 1: "errInfo" is propagated - */ - public function testErrInfoIsPropagated() - { - $runOn = [(object) ['minServerVersion' => '4.0.0']]; - $this->checkServerRequirements($runOn); - - $errInfo = (object) [ - 'writeConcern' => (object) [ - 'w' => 2, - 'wtimeout' => 0, - 'provenance' => 'clientSupplied', - ], - ]; - - $this->configureFailPoint([ - 'configureFailPoint' => 'failCommand', - 'mode' => ['times' => 1], - 'data' => [ - 'failCommands' => ['insert'], - 'writeConcernError' => [ - 'code' => 100, - 'codeName' => 'UnsatisfiableWriteConcern', - 'errmsg' => 'Not enough data-bearing nodes', - 'errInfo' => $errInfo, - ], - ], - ]); - - $client = new Client(static::getUri()); - - try { - $client->selectCollection($this->getDatabaseName(), $this->getCollectionName())->insertOne(['fail' => 1]); - $this->fail('Expected insert command to fail'); - } catch (BulkWriteException $e) { - self::assertEquals($errInfo, $e->getWriteResult()->getWriteConcernError()->getInfo()); - } - } -} diff --git a/tests/SpecTests/DocumentsMatchConstraint.php b/tests/SpecTests/DocumentsMatchConstraint.php index cfce06d89..cf5f98ee7 100644 --- a/tests/SpecTests/DocumentsMatchConstraint.php +++ b/tests/SpecTests/DocumentsMatchConstraint.php @@ -4,43 +4,31 @@ use ArrayObject; use InvalidArgumentException; -use MongoDB\BSON\BinaryInterface; -use MongoDB\BSON\DBPointer; -use MongoDB\BSON\Decimal128; use MongoDB\BSON\Int64; -use MongoDB\BSON\Javascript; -use MongoDB\BSON\MaxKey; -use MongoDB\BSON\MinKey; -use MongoDB\BSON\ObjectId; -use MongoDB\BSON\Regex; -use MongoDB\BSON\Symbol; -use MongoDB\BSON\Timestamp; -use MongoDB\BSON\Undefined; -use MongoDB\BSON\UTCDateTime; use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; +use MongoDB\Tests\UnifiedSpecTests\Constraint\IsBsonType; use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\Constraint\IsInstanceOf; -use PHPUnit\Framework\Constraint\IsNull; -use PHPUnit\Framework\Constraint\IsType; -use PHPUnit\Framework\Constraint\LogicalAnd; -use PHPUnit\Framework\Constraint\LogicalNot; -use PHPUnit\Framework\Constraint\LogicalOr; use RuntimeException; use SebastianBergmann\Comparator\ComparisonFailure; use SebastianBergmann\Comparator\Factory; use stdClass; use Symfony\Bridge\PhpUnit\ConstraintTrait; + use function array_values; use function get_class; use function get_debug_type; -use function in_array; use function is_array; +use function is_float; +use function is_int; use function is_object; -use function is_scalar; -use function method_exists; +use function PHPUnit\Framework\assertThat; +use function PHPUnit\Framework\containsOnly; +use function PHPUnit\Framework\isInstanceOf; +use function PHPUnit\Framework\isType; +use function PHPUnit\Framework\logicalAnd; +use function PHPUnit\Framework\logicalOr; use function sprintf; -use const PHP_INT_SIZE; /** * Constraint that checks if one document matches another. @@ -57,9 +45,6 @@ class DocumentsMatchConstraint extends Constraint /** @var boolean */ private $ignoreExtraKeysInEmbedded = false; - /** @var array */ - private $placeholders = []; - /** * TODO: This is not currently used, but was preserved from the design of * TestCase::assertMatchesDocument(), which would sort keys and then compare @@ -86,14 +71,12 @@ class DocumentsMatchConstraint extends Constraint * @param array|object $value * @param boolean $ignoreExtraKeysInRoot If true, ignore extra keys within the root document * @param boolean $ignoreExtraKeysInEmbedded If true, ignore extra keys within embedded documents - * @param array $placeholders Placeholders for any value */ - public function __construct($value, $ignoreExtraKeysInRoot = false, $ignoreExtraKeysInEmbedded = false, array $placeholders = []) + public function __construct($value, bool $ignoreExtraKeysInRoot = false, bool $ignoreExtraKeysInEmbedded = false) { $this->value = $this->prepareBSON($value, true, $this->sortKeys); $this->ignoreExtraKeysInRoot = $ignoreExtraKeysInRoot; $this->ignoreExtraKeysInEmbedded = $ignoreExtraKeysInEmbedded; - $this->placeholders = $placeholders; $this->comparatorFactory = Factory::getInstance(); } @@ -135,137 +118,26 @@ private function doEvaluate($other, $description = '', $returnResult = false) } /** - * @param string $expectedType - * @param mixed $actualValue + * @param string|string[] $expectedType + * @param mixed $actualValue */ - private function assertBSONType($expectedType, $actualValue) + private function assertBSONType($expectedType, $actualValue): void { - switch ($expectedType) { - case 'double': - (new IsType('float'))->evaluate($actualValue); - - return; - case 'string': - (new IsType('string'))->evaluate($actualValue); - - return; - case 'object': - $constraints = [ - new IsType('object'), - new LogicalNot(new IsInstanceOf(BSONArray::class)), - ]; - - // LogicalAnd::fromConstraints was introduced in PHPUnit 6.5.0. - // This check can be removed when the PHPUnit dependency is bumped to that version - if (method_exists(LogicalAnd::class, 'fromConstraints')) { - $constraint = LogicalAnd::fromConstraints(...$constraints); - } else { - $constraint = new LogicalAnd(); - $constraint->setConstraints($constraints); - } - - $constraint->evaluate($actualValue); - - return; - case 'array': - $constraints = [ - new IsType('array'), - new IsInstanceOf(BSONArray::class), - ]; - - // LogicalOr::fromConstraints was introduced in PHPUnit 6.5.0. - // This check can be removed when the PHPUnit dependency is bumped to that version - if (method_exists(LogicalOr::class, 'fromConstraints')) { - $constraint = LogicalOr::fromConstraints(...$constraints); - } else { - $constraint = new LogicalOr(); - $constraint->setConstraints($constraints); - } - - $constraint->evaluate($actualValue); - - return; - case 'binData': - (new IsInstanceOf(BinaryInterface::class))->evaluate($actualValue); - - return; - case 'undefined': - (new IsInstanceOf(Undefined::class))->evaluate($actualValue); - - return; - case 'objectId': - (new IsInstanceOf(ObjectId::class))->evaluate($actualValue); - - return; - case 'boolean': - (new IsType('bool'))->evaluate($actualValue); - - return; - case 'date': - (new IsInstanceOf(UTCDateTime::class))->evaluate($actualValue); - - return; - case 'null': - (new IsNull())->evaluate($actualValue); - - return; - case 'regex': - (new IsInstanceOf(Regex::class))->evaluate($actualValue); - - return; - case 'dbPointer': - (new IsInstanceOf(DBPointer::class))->evaluate($actualValue); - - return; - case 'javascript': - (new IsInstanceOf(Javascript::class))->evaluate($actualValue); - - return; - case 'symbol': - (new IsInstanceOf(Symbol::class))->evaluate($actualValue); - - return; - case 'int': - (new IsType('int'))->evaluate($actualValue); - - return; - case 'timestamp': - (new IsInstanceOf(Timestamp::class))->evaluate($actualValue); - - return; - case 'long': - if (PHP_INT_SIZE == 4) { - (new IsInstanceOf(Int64::class))->evaluate($actualValue); - } else { - (new IsType('int'))->evaluate($actualValue); - } - - return; - case 'decimal': - (new IsInstanceOf(Decimal128::class))->evaluate($actualValue); - - return; - case 'minKey': - (new IsInstanceOf(MinKey::class))->evaluate($actualValue); - - return; - case 'maxKey': - (new IsInstanceOf(MaxKey::class))->evaluate($actualValue); - - return; - } + assertThat( + $expectedType, + logicalOr(isType('string'), logicalAnd(isInstanceOf(BSONArray::class), containsOnly('string'))), + '$$type requires string or string[]' + ); + + IsBsonType::anyOf(...(array) $expectedType)->evaluate($actualValue); } /** * Compares two documents recursively. * - * @param ArrayObject $expected - * @param ArrayObject $actual - * @param boolean $ignoreExtraKeys - * @param string $keyPrefix * @throws RuntimeException if the documents do not match */ - private function assertEquals(ArrayObject $expected, ArrayObject $actual, $ignoreExtraKeys, $keyPrefix = '') + private function assertEquals(ArrayObject $expected, ArrayObject $actual, bool $ignoreExtraKeys, string $keyPrefix = ''): void { if (get_class($expected) !== get_class($actual)) { throw new RuntimeException(sprintf( @@ -282,10 +154,6 @@ private function assertEquals(ArrayObject $expected, ArrayObject $actual, $ignor throw new RuntimeException(sprintf('$actual is missing key: "%s"', $keyPrefix . $key)); } - if (in_array($expectedValue, $this->placeholders, true)) { - continue; - } - $actualValue = $actual[$key]; if ($expectedValue instanceof BSONDocument && isset($expectedValue['$$type'])) { @@ -293,32 +161,21 @@ private function assertEquals(ArrayObject $expected, ArrayObject $actual, $ignor continue; } - if (($expectedValue instanceof BSONArray && $actualValue instanceof BSONArray) || - ($expectedValue instanceof BSONDocument && $actualValue instanceof BSONDocument)) { + if ( + ($expectedValue instanceof BSONArray && $actualValue instanceof BSONArray) || + ($expectedValue instanceof BSONDocument && $actualValue instanceof BSONDocument) + ) { $this->assertEquals($expectedValue, $actualValue, $this->ignoreExtraKeysInEmbedded, $keyPrefix . $key . '.'); continue; } - if (is_scalar($expectedValue) && is_scalar($actualValue)) { - if ($expectedValue !== $actualValue) { - throw new ComparisonFailure( - $expectedValue, - $actualValue, - '', - '', - false, - sprintf('Field path "%s": %s', $keyPrefix . $key, 'Failed asserting that two values are equal.') - ); - } - - continue; - } - $expectedType = get_debug_type($expectedValue); $actualType = get_debug_type($actualValue); - // Workaround for ObjectComparator printing the whole actual object - if ($expectedType !== $actualType) { + /* Early check to work around ObjectComparator printing the entire value + * for a failed type comparison. Avoid doing this if either value is + * numeric to allow for flexible numeric comparisons (e.g. 1 == 1.0). */ + if ($expectedType !== $actualType && ! (self::isNumeric($expectedValue) || self::isNumeric($actualValue))) { throw new ComparisonFailure( $expectedValue, $actualValue, @@ -398,6 +255,11 @@ private function doToString() return 'matches ' . $this->exporter()->export($this->value); } + private static function isNumeric($value): bool + { + return is_int($value) || is_float($value) || $value instanceof Int64; + } + /** * Prepare a BSON document or array for comparison. * @@ -406,12 +268,11 @@ private function doToString() * value within the array or document will then be prepared recursively. * * @param array|object $bson - * @param boolean $isRoot If true, ensure an array value is converted to a document - * @param boolean $sortKeys + * @param boolean $isRoot If true, ensure an array value is converted to a document * @return BSONDocument|BSONArray * @throws InvalidArgumentException if $bson is not an array or object */ - private function prepareBSON($bson, $isRoot, $sortKeys = false) + private function prepareBSON($bson, bool $isRoot, bool $sortKeys = false) { if (! is_array($bson) && ! is_object($bson)) { throw new InvalidArgumentException('$bson is not an array or object'); @@ -445,12 +306,6 @@ private function prepareBSON($bson, $isRoot, $sortKeys = false) $bson[$key] = $this->prepareBSON($value, false, $sortKeys); continue; } - - /* Convert Int64 objects to integers on 64-bit platforms for - * compatibility reasons. */ - if ($value instanceof Int64 && PHP_INT_SIZE != 4) { - $bson[$key] = (int) ((string) $value); - } } return $bson; diff --git a/tests/SpecTests/DocumentsMatchConstraintTest.php b/tests/SpecTests/DocumentsMatchConstraintTest.php index 1d4abddfe..c14f001c6 100644 --- a/tests/SpecTests/DocumentsMatchConstraintTest.php +++ b/tests/SpecTests/DocumentsMatchConstraintTest.php @@ -4,6 +4,7 @@ use MongoDB\BSON\Binary; use MongoDB\BSON\Decimal128; +use MongoDB\BSON\Int64; use MongoDB\BSON\Javascript; use MongoDB\BSON\MaxKey; use MongoDB\BSON\MinKey; @@ -15,14 +16,15 @@ use MongoDB\Model\BSONDocument; use MongoDB\Tests\TestCase; use PHPUnit\Framework\ExpectationFailedException; + use function MongoDB\BSON\fromJSON; use function MongoDB\BSON\toPHP; -use function unserialize; + use const PHP_INT_SIZE; class DocumentsMatchConstraintTest extends TestCase { - public function testIgnoreExtraKeysInRoot() + public function testIgnoreExtraKeysInRoot(): void { $c = new DocumentsMatchConstraint(['x' => 1, 'y' => ['a' => 1, 'b' => 2]], true, false); @@ -41,7 +43,15 @@ public function testIgnoreExtraKeysInRoot() $this->assertResult(false, $c, [1, ['a' => 1, 'b' => 2]], 'Extra keys in embedded are not permitted'); } - public function testIgnoreExtraKeysInEmbedded() + public function testFlexibleNumericComparison(): void + { + $c = new DocumentsMatchConstraint(['x' => 1, 'y' => 1.0]); + $this->assertResult(true, $c, ['x' => 1.0, 'y' => 1.0], 'Float instead of expected int matches'); + $this->assertResult(true, $c, ['x' => 1, 'y' => 1], 'Int instead of expected float matches'); + $this->assertResult(false, $c, ['x' => 'foo', 'y' => 1.0], 'Different type does not match'); + } + + public function testIgnoreExtraKeysInEmbedded(): void { $c = new DocumentsMatchConstraint(['x' => 1, 'y' => ['a' => 1, 'b' => 2]], false, true); @@ -62,19 +72,8 @@ public function testIgnoreExtraKeysInEmbedded() $this->assertResult(false, $c, [1, ['a' => 2]], 'Keys must have the correct value'); } - public function testPlaceholders() - { - $c = new DocumentsMatchConstraint(['x' => '42', 'y' => 42, 'z' => ['a' => 24]], false, false, [24, 42]); - - $this->assertResult(true, $c, ['x' => '42', 'y' => 'foo', 'z' => ['a' => 1]], 'Placeholders accept any value'); - $this->assertResult(false, $c, ['x' => 42, 'y' => 'foo', 'z' => ['a' => 1]], 'Placeholder type must match'); - $this->assertResult(true, $c, ['x' => '42', 'y' => 42, 'z' => ['a' => 24]], 'Exact match'); - } - - /** - * @dataProvider provideBSONTypes - */ - public function testBSONTypeAssertions($type, $value) + /** @dataProvider provideBSONTypes */ + public function testBSONTypeAssertions($type, $value): void { $constraint = new DocumentsMatchConstraint(['x' => ['$$type' => $type]]); @@ -83,9 +82,11 @@ public function testBSONTypeAssertions($type, $value) public function provideBSONTypes() { - $undefined = toPHP(fromJSON('{ "undefined": {"$undefined": true} }')); - $symbol = toPHP(fromJSON('{ "symbol": {"$symbol": "test"} }')); - $dbPointer = toPHP(fromJSON('{ "dbPointer": {"$dbPointer": {"$ref": "phongo.test", "$id" : { "$oid" : "5a2e78accd485d55b405ac12" } }} }')); + $undefined = toPHP(fromJSON('{ "x": {"$undefined": true} }'))->x; + $symbol = toPHP(fromJSON('{ "x": {"$symbol": "test"} }'))->x; + $dbPointer = toPHP(fromJSON('{ "x": {"$dbPointer": {"$ref": "db.coll", "$id" : { "$oid" : "5a2e78accd485d55b405ac12" } }} }'))->x; + $int64 = new Int64(1); + $long = PHP_INT_SIZE == 4 ? new Int64('4294967296') : 4294967296; return [ 'double' => ['double', 1.4], @@ -93,28 +94,48 @@ public function provideBSONTypes() 'object' => ['object', new BSONDocument()], 'array' => ['array', ['foo']], 'binData' => ['binData', new Binary('', 0)], - 'undefined' => ['undefined', $undefined->undefined], + 'undefined' => ['undefined', $undefined], 'objectId' => ['objectId', new ObjectId()], - 'boolean' => ['boolean', true], + 'bool' => ['bool', true], 'date' => ['date', new UTCDateTime()], 'null' => ['null', null], 'regex' => ['regex', new Regex('.*')], - 'dbPointer' => ['dbPointer', $dbPointer->dbPointer], + 'dbPointer' => ['dbPointer', $dbPointer], 'javascript' => ['javascript', new Javascript('foo = 1;')], - 'symbol' => ['symbol', $symbol->symbol], + 'symbol' => ['symbol', $symbol], 'int' => ['int', 1], 'timestamp' => ['timestamp', new Timestamp(0, 0)], - 'long' => ['long', PHP_INT_SIZE == 4 ? unserialize('C:18:"MongoDB\BSON\Int64":38:{a:1:{s:7:"integer";s:10:"4294967296";}}') : 4294967296], + 'long(int64)' => ['long', $int64], + 'long(long)' => ['long', $long], 'decimal' => ['decimal', new Decimal128('18446744073709551616')], 'minKey' => ['minKey', new MinKey()], 'maxKey' => ['maxKey', new MaxKey()], + 'number(double)' => ['number', 1.4], + 'number(decimal)' => ['number', new Decimal128('18446744073709551616')], + 'number(int)' => ['number', 1], + 'number(int64)' => ['number', $int64], + 'number(long)' => ['number', $long], ]; } - /** - * @dataProvider errorMessageProvider - */ - public function testErrorMessages($expectedMessagePart, DocumentsMatchConstraint $constraint, $actualValue) + public function testBSONTypeAssertionsWithMultipleTypes(): void + { + $c1 = new DocumentsMatchConstraint(['x' => ['$$type' => ['double', 'int']]]); + + $this->assertResult(true, $c1, ['x' => 1], 'int is double or int'); + $this->assertResult(true, $c1, ['x' => 1.4], 'double is double or int'); + $this->assertResult(false, $c1, ['x' => 'foo'], 'string is not double or int'); + + $c2 = new DocumentsMatchConstraint(['x' => ['$$type' => ['number', 'string']]]); + + $this->assertResult(true, $c2, ['x' => 1], 'int is number or string'); + $this->assertResult(true, $c2, ['x' => 1.4], 'double is number or string'); + $this->assertResult(true, $c2, ['x' => 'foo'], 'string is number or string'); + $this->assertResult(false, $c2, ['x' => true], 'bool is not number or string'); + } + + /** @dataProvider errorMessageProvider */ + public function testErrorMessages($expectedMessagePart, DocumentsMatchConstraint $constraint, $actualValue): void { try { $constraint->evaluate($actualValue); @@ -144,13 +165,13 @@ public function errorMessageProvider() ['foo' => ['foo' => 'bar', 'bar' => 'baz']], ], 'Scalar value not equal' => [ - 'Field path "foo": Failed asserting that two values are equal.', + 'Field path "foo": Failed asserting that two strings are equal.', new DocumentsMatchConstraint(['foo' => 'bar']), ['foo' => 'baz'], ], 'Scalar type mismatch' => [ - 'Field path "foo": Failed asserting that two values are equal.', - new DocumentsMatchConstraint(['foo' => 42]), + 'Field path "foo": \'42\' is not instance of expected type "bool".', + new DocumentsMatchConstraint(['foo' => true]), ['foo' => '42'], ], 'Type mismatch' => [ @@ -161,7 +182,7 @@ public function errorMessageProvider() ]; } - private function assertResult($expectedResult, DocumentsMatchConstraint $constraint, $value, $message) + private function assertResult($expectedResult, DocumentsMatchConstraint $constraint, $value, $message): void { $this->assertSame($expectedResult, $constraint->evaluate($value, '', true), $message); } diff --git a/tests/SpecTests/ErrorExpectation.php b/tests/SpecTests/ErrorExpectation.php index 6e0c03806..56d271427 100644 --- a/tests/SpecTests/ErrorExpectation.php +++ b/tests/SpecTests/ErrorExpectation.php @@ -11,6 +11,7 @@ use MongoDB\Tests\TestCase; use stdClass; use Throwable; + use function get_class; use function is_array; use function is_string; @@ -56,26 +57,6 @@ private function __construct() { } - public static function fromChangeStreams(stdClass $result) - { - $o = new self(); - - if (isset($result->error->code)) { - $o->code = $result->error->code; - $o->isExpected = true; - } - - if (isset($result->error->errorLabels)) { - if (! self::isArrayOfStrings($result->error->errorLabels)) { - throw InvalidArgumentException::invalidType('errorLabels', $result->error->errorLabels, 'string[]'); - } - $o->includedLabels = $result->error->errorLabels; - $o->isExpected = true; - } - - return $o; - } - public static function fromClientSideEncryption(stdClass $operation) { return self::fromGenericOperation($operation); @@ -122,6 +103,7 @@ public static function fromRetryableWrites(stdClass $outcome) if (! self::isArrayOfStrings($outcome->result->errorLabelsContain)) { throw InvalidArgumentException::invalidType('errorLabelsContain', $outcome->result->errorLabelsContain, 'string[]'); } + $o->includedLabels = $outcome->result->errorLabelsContain; } @@ -129,15 +111,14 @@ public static function fromRetryableWrites(stdClass $outcome) if (! self::isArrayOfStrings($outcome->result->errorLabelsOmit)) { throw InvalidArgumentException::invalidType('errorLabelsOmit', $outcome->result->errorLabelsOmit, 'string[]'); } + $o->excludedLabels = $outcome->result->errorLabelsOmit; } return $o; } - /** - * @throws InvalidArgumentException - */ + /** @throws InvalidArgumentException */ public static function fromTransactions(stdClass $operation) { return self::fromGenericOperation($operation); @@ -154,13 +135,15 @@ public static function noError() * @param TestCase $test Test instance for performing assertions * @param Exception|null $actual Exception (if any) from the actual outcome */ - public function assert(TestCase $test, Throwable $actual = null) + public function assert(TestCase $test, ?Throwable $actual = null): void { if (! $this->isExpected) { if ($actual !== null) { $test->fail(sprintf("Operation threw unexpected %s: %s\n%s", get_class($actual), $actual->getMessage(), $actual->getTraceAsString())); } + $test->addToAssertionCount(1); + return; } @@ -198,7 +181,7 @@ public function isExpected() * @param TestCase $test Test instance for performing assertions * @param Exception|null $actual Exception (if any) from the actual outcome */ - private function assertCodeName(TestCase $test, Throwable $actual = null) + private function assertCodeName(TestCase $test, ?Throwable $actual = null): void { /* BulkWriteException does not expose codeName for server errors. Work * around this be comparing the error code against a map. @@ -225,9 +208,7 @@ private function assertCodeName(TestCase $test, Throwable $actual = null) $test->assertSame($this->codeName, $result->codeName); } - /** - * @throws InvalidArgumentException - */ + /** @throws InvalidArgumentException */ private static function fromGenericOperation(stdClass $operation) { $o = new self(); @@ -252,6 +233,7 @@ private static function fromGenericOperation(stdClass $operation) if (! self::isArrayOfStrings($result->errorLabelsContain)) { throw InvalidArgumentException::invalidType('errorLabelsContain', $result->errorLabelsContain, 'string[]'); } + $o->includedLabels = $result->errorLabelsContain; $o->isExpected = true; } @@ -260,6 +242,7 @@ private static function fromGenericOperation(stdClass $operation) if (! self::isArrayOfStrings($result->errorLabelsOmit)) { throw InvalidArgumentException::invalidType('errorLabelsOmit', $result->errorLabelsOmit, 'string[]'); } + $o->excludedLabels = $result->errorLabelsOmit; $o->isExpected = true; } diff --git a/tests/SpecTests/FunctionalTestCase.php b/tests/SpecTests/FunctionalTestCase.php index 1c0a76803..ffab8a20a 100644 --- a/tests/SpecTests/FunctionalTestCase.php +++ b/tests/SpecTests/FunctionalTestCase.php @@ -3,7 +3,6 @@ namespace MongoDB\Tests\SpecTests; use ArrayIterator; -use IteratorIterator; use LogicException; use MongoDB\Collection; use MongoDB\Driver\Server; @@ -11,8 +10,8 @@ use MultipleIterator; use PHPUnit\Framework\SkippedTest; use stdClass; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; use UnexpectedValueException; + use function in_array; use function json_encode; use function MongoDB\BSON\fromJSON; @@ -27,23 +26,26 @@ */ class FunctionalTestCase extends BaseFunctionalTestCase { - use SetUpTearDownTrait; + public const TOPOLOGY_SINGLE = 'single'; + public const TOPOLOGY_REPLICASET = 'replicaset'; + public const TOPOLOGY_SHARDED = 'sharded'; + public const TOPOLOGY_LOAD_BALANCED = 'load-balanced'; - const TOPOLOGY_SINGLE = 'single'; - const TOPOLOGY_REPLICASET = 'replicaset'; - const TOPOLOGY_SHARDED = 'sharded'; + public const SERVERLESS_ALLOW = 'allow'; + public const SERVERLESS_FORBID = 'forbid'; + public const SERVERLESS_REQUIRE = 'require'; /** @var Context|null */ private $context; - private function doSetUp() + public function setUp(): void { parent::setUp(); $this->context = null; } - private function doTearDown() + public function tearDown(): void { $this->context = null; @@ -59,7 +61,7 @@ private function doTearDown() * @param stdClass $expectedCommand Expected command document * @param stdClass $actualCommand Actual command document */ - public static function assertCommandMatches(stdClass $expected, stdClass $actual) + public static function assertCommandMatches(stdClass $expected, stdClass $actual): void { throw new LogicException(sprintf('%s does not assert CommandStartedEvents', static::class)); } @@ -73,7 +75,7 @@ public static function assertCommandMatches(stdClass $expected, stdClass $actual * @param stdClass $expected Expected command reply document * @param stdClass $actual Actual command reply document */ - public static function assertCommandReplyMatches(stdClass $expected, stdClass $actual) + public static function assertCommandReplyMatches(stdClass $expected, stdClass $actual): void { throw new LogicException(sprintf('%s does not assert CommandSucceededEvents', static::class)); } @@ -85,9 +87,8 @@ public static function assertCommandReplyMatches(stdClass $expected, stdClass $a * * @param array|object $expectedDocument * @param array|object $actualDocument - * @param string $message */ - protected static function assertDocumentsMatch($expectedDocument, $actualDocument, $message = '') + public static function assertDocumentsMatch($expectedDocument, $actualDocument, string $message = ''): void { $constraint = new DocumentsMatchConstraint($expectedDocument, true, true); @@ -95,21 +96,36 @@ protected static function assertDocumentsMatch($expectedDocument, $actualDocumen } /** - * Assert data within the outcome collection. + * Assert omitted top-level fields in command documents. * - * @param array $expectedDocuments - * @param int $resultExpectation + * Note: this method may modify the $expected object. + * + * @see https://github.com/mongodb/specifications/blob/master/source/transactions/tests/README.rst#null-values + * @see https://github.com/mongodb/specifications/blob/09ee1ebc481f1502e3246971a9419e484d736207/source/command-monitoring/tests/README.rst#additional-values */ - protected function assertOutcomeCollectionData(array $expectedDocuments, $resultExpectation = ResultExpectation::ASSERT_SAME_DOCUMENT) + protected static function assertCommandOmittedFields(stdClass $expected, stdClass $actual): void + { + foreach ($expected as $key => $value) { + if ($value === null) { + static::assertObjectNotHasAttribute($key, $actual); + unset($expected->{$key}); + } + } + } + + /** + * Assert data within the outcome collection. + */ + protected function assertOutcomeCollectionData(array $expectedDocuments, int $resultExpectation = ResultExpectation::ASSERT_SAME_DOCUMENT): void { $outcomeCollection = $this->getOutcomeCollection($this->getContext()->outcomeReadOptions); $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); $mi->attachIterator(new ArrayIterator($expectedDocuments)); - $mi->attachIterator(new IteratorIterator($outcomeCollection->find([], ['sort' => ['_id' => 1]]))); + $mi->attachIterator($outcomeCollection->find([], ['sort' => ['_id' => 1]])); foreach ($mi as $documents) { - list($expectedDocument, $actualDocument) = $documents; + [$expectedDocument, $actualDocument] = $documents; $this->assertNotNull($expectedDocument); $this->assertNotNull($actualDocument); @@ -131,17 +147,17 @@ protected function assertOutcomeCollectionData(array $expectedDocuments, $result /** * Checks server version and topology requirements. * - * @param array $runOn * @throws SkippedTest if the server requirements are not met */ - protected function checkServerRequirements(array $runOn) + protected function checkServerRequirements(array $runOn): void { foreach ($runOn as $req) { $minServerVersion = $req->minServerVersion ?? null; $maxServerVersion = $req->maxServerVersion ?? null; $topologies = $req->topology ?? null; + $serverlessMode = $req->serverless ?? null; - if ($this->isServerRequirementSatisifed($minServerVersion, $maxServerVersion, $topologies)) { + if ($this->isServerRequirementSatisifed($minServerVersion, $maxServerVersion, $topologies, $serverlessMode)) { return; } } @@ -158,10 +174,9 @@ protected function checkServerRequirements(array $runOn) * This decodes the file through the driver's extended JSON parser to ensure * proper handling of special types. * - * @param string $json - * @return array + * @return array|object */ - protected function decodeJson($json) + protected function decodeJson(string $json) { return toPHP(fromJSON($json)); } @@ -169,10 +184,9 @@ protected function decodeJson($json) /** * Return the test context. * - * @return Context * @throws LogicException if the context has not been set */ - protected function getContext() + protected function getContext(): Context { if (! $this->context instanceof Context) { throw new LogicException('Context has not been set'); @@ -183,10 +197,8 @@ protected function getContext() /** * Set the test context. - * - * @param Context $context */ - protected function setContext(Context $context) + protected function setContext(Context $context): void { $this->context = $context; } @@ -194,7 +206,7 @@ protected function setContext(Context $context) /** * Drop the test and outcome collections by dropping them. */ - protected function dropTestAndOutcomeCollections() + protected function dropTestAndOutcomeCollections(array $testCollectionDropOptions = []): void { $context = $this->getContext(); @@ -210,7 +222,7 @@ protected function dropTestAndOutcomeCollections() $collection = null; if ($context->collectionName !== null) { $collection = $context->getCollection($context->defaultWriteOptions); - $collection->drop(); + $collection->drop($testCollectionDropOptions); } if ($context->outcomeCollectionName !== null) { @@ -225,11 +237,8 @@ protected function dropTestAndOutcomeCollections() /** * Insert data fixtures into the test collection. - * - * @param array $documents - * @param string|null $collectionName */ - protected function insertDataFixtures(array $documents, $collectionName = null) + protected function insertDataFixtures(array $documents, ?string $collectionName = null): void { if (empty($documents)) { return; @@ -254,15 +263,15 @@ private function getOutcomeCollection(array $collectionOptions = []) /** * Return the corresponding topology constants for the current topology. * - * @return string * @throws UnexpectedValueException if topology is neither single nor RS nor sharded */ - private function getTopology() + private function getTopology(): string { $topologyTypeMap = [ Server::TYPE_STANDALONE => self::TOPOLOGY_SINGLE, Server::TYPE_RS_PRIMARY => self::TOPOLOGY_REPLICASET, Server::TYPE_MONGOS => self::TOPOLOGY_SHARDED, + Server::TYPE_LOAD_BALANCER => self::TOPOLOGY_LOAD_BALANCED, ]; $primaryType = $this->getPrimaryServer()->getType(); @@ -271,18 +280,33 @@ private function getTopology() return $topologyTypeMap[$primaryType]; } - throw new UnexpectedValueException('Toplogy is neither single nor RS nor sharded'); + throw new UnexpectedValueException(sprintf('Cannot find topology for primary of type "%d".', $primaryType)); + } + + private function isServerlessRequirementSatisfied(?string $serverlessMode): bool + { + if ($serverlessMode === null) { + return true; + } + + switch ($serverlessMode) { + case self::SERVERLESS_ALLOW: + return true; + + case self::SERVERLESS_FORBID: + return ! static::isServerless(); + + case self::SERVERLESS_REQUIRE: + return static::isServerless(); + } + + throw new UnexpectedValueException(sprintf('Invalid serverless requirement "%s" found.', $serverlessMode)); } /** * Checks if server version and topology requirements are satifised. - * - * @param string|null $minServerVersion - * @param string|null $maxServerVersion - * @param array|null $topologies - * @return boolean */ - private function isServerRequirementSatisifed($minServerVersion, $maxServerVersion, array $topologies = null) + private function isServerRequirementSatisifed(?string $minServerVersion, ?string $maxServerVersion, ?array $topologies = null, ?string $serverlessMode = null): bool { $serverVersion = $this->getServerVersion(); @@ -300,6 +324,10 @@ private function isServerRequirementSatisifed($minServerVersion, $maxServerVersi return false; } + if (! $this->isServerlessRequirementSatisfied($serverlessMode)) { + return false; + } + return true; } } diff --git a/tests/SpecTests/Operation.php b/tests/SpecTests/Operation.php index bd34e735f..9d299bd7b 100644 --- a/tests/SpecTests/Operation.php +++ b/tests/SpecTests/Operation.php @@ -11,12 +11,13 @@ use MongoDB\Driver\Exception\Exception; use MongoDB\Driver\Server; use MongoDB\Driver\Session; -use MongoDB\Driver\WriteConcern; use MongoDB\GridFS\Bucket; +use MongoDB\MapReduceResult; use MongoDB\Model\IndexInfo; use MongoDB\Operation\FindOneAndReplace; use MongoDB\Operation\FindOneAndUpdate; use stdClass; + use function array_diff_key; use function array_map; use function fclose; @@ -32,15 +33,15 @@ */ final class Operation { - const OBJECT_CLIENT = 'client'; - const OBJECT_COLLECTION = 'collection'; - const OBJECT_DATABASE = 'database'; - const OBJECT_GRIDFS_BUCKET = 'gridfsbucket'; - const OBJECT_SELECT_COLLECTION = 'selectCollection'; - const OBJECT_SELECT_DATABASE = 'selectDatabase'; - const OBJECT_SESSION0 = 'session0'; - const OBJECT_SESSION1 = 'session1'; - const OBJECT_TEST_RUNNER = 'testRunner'; + public const OBJECT_CLIENT = 'client'; + public const OBJECT_COLLECTION = 'collection'; + public const OBJECT_DATABASE = 'database'; + public const OBJECT_GRIDFS_BUCKET = 'gridfsbucket'; + public const OBJECT_SELECT_COLLECTION = 'selectCollection'; + public const OBJECT_SELECT_DATABASE = 'selectDatabase'; + public const OBJECT_SESSION0 = 'session0'; + public const OBJECT_SESSION1 = 'session1'; + public const OBJECT_TEST_RUNNER = 'testRunner'; /** @var ErrorExpectation|null */ public $errorExpectation; @@ -82,45 +83,6 @@ private function __construct(stdClass $operation) } } - public static function fromChangeStreams(stdClass $operation) - { - $o = new self($operation); - - /* Note: change streams only return majority-committed writes, so ensure - * each operation applies that write concern. This will avoid spurious - * test failures. */ - $writeConcern = new WriteConcern(WriteConcern::MAJORITY); - - // Expect all operations to succeed - $o->errorExpectation = ErrorExpectation::noError(); - - /* The Change Streams spec tests include a unique "rename" operation, - * which we should convert to a renameCollection command to be run - * against the admin database. */ - if ($operation->name === 'rename') { - $o->object = self::OBJECT_SELECT_DATABASE; - $o->databaseName = 'admin'; - $o->name = 'runCommand'; - $o->arguments = [ - 'command' => [ - 'renameCollection' => $operation->database . '.' . $operation->collection, - 'to' => $operation->database . '.' . $operation->arguments->to, - // Note: Database::command() does not inherit WC, so be explicit - 'writeConcern' => $writeConcern, - ], - ]; - - return $o; - } - - $o->databaseName = $operation->database; - $o->collectionName = $operation->collection; - $o->collectionOptions = ['writeConcern' => $writeConcern]; - $o->object = self::OBJECT_SELECT_COLLECTION; - - return $o; - } - public static function fromClientSideEncryption(stdClass $operation) { $o = new self($operation); @@ -135,27 +97,11 @@ public static function fromClientSideEncryption(stdClass $operation) return $o; } - public static function fromCommandMonitoring(stdClass $operation) - { - $o = new self($operation); - - if (isset($operation->collectionOptions)) { - $o->collectionOptions = (array) $operation->collectionOptions; - } - - /* We purposefully avoid setting a default error expectation, because - * some tests may trigger a write or command error. */ - - return $o; - } - /** * This method is exclusively used to prepare nested operations for the * withTransaction session operation - * - * @return Operation */ - private static function fromConvenientTransactions(stdClass $operation) + private static function fromConvenientTransactions(stdClass $operation): Operation { $o = new self($operation); @@ -241,11 +187,9 @@ public static function fromTransactions(stdClass $operation) /** * Execute the operation and assert its outcome. * - * @param FunctionalTestCase $test Test instance - * @param Context $context Execution context - * @param bool $bubbleExceptions If true, any exception that was caught is rethrown + * @param bool $bubbleExceptions If true, any exception that was caught is rethrown */ - public function assert(FunctionalTestCase $test, Context $context, $bubbleExceptions = false) + public function assert(FunctionalTestCase $test, Context $context, bool $bubbleExceptions = false): void { $result = null; $exception = null; @@ -259,6 +203,10 @@ public function assert(FunctionalTestCase $test, Context $context, $bubbleExcept * is not used (e.g. Command Monitoring spec). */ if ($result instanceof Cursor) { $result = $result->toArray(); + } elseif ($result instanceof MapReduceResult) { + /* For mapReduce operations, we ignore the mapReduce metadata + * and only return the result iterator for evaluation. */ + $result = iterator_to_array($result->getIterator()); } } catch (Exception $e) { $exception = $e; @@ -285,7 +233,6 @@ public function assert(FunctionalTestCase $test, Context $context, $bubbleExcept /** * Executes the operation with a given context. * - * @param Context $context Execution context * @return mixed * @throws LogicException if the operation is unsupported */ @@ -296,32 +243,41 @@ private function execute(FunctionalTestCase $test, Context $context) $client = $context->getClient(); return $this->executeForClient($client, $context); + case self::OBJECT_COLLECTION: $collection = $context->getCollection($this->collectionOptions, $this->databaseOptions); return $this->executeForCollection($collection, $context); + case self::OBJECT_DATABASE: $database = $context->getDatabase(); return $this->executeForDatabase($database, $context); + case self::OBJECT_GRIDFS_BUCKET: $bucket = $context->getGridFSBucket(); return $this->executeForGridFSBucket($bucket, $context); + case self::OBJECT_SELECT_COLLECTION: $collection = $context->selectCollection($this->databaseName, $this->collectionName, $this->collectionOptions, $this->databaseOptions); return $this->executeForCollection($collection, $context); + case self::OBJECT_SELECT_DATABASE: $database = $context->selectDatabase($this->databaseName); return $this->executeForDatabase($database, $context); + case self::OBJECT_SESSION0: return $this->executeForSession($context->session0, $test, $context); + case self::OBJECT_SESSION1: return $this->executeForSession($context->session1, $test, $context); + case self::OBJECT_TEST_RUNNER: return $this->executeForTestRunner($test, $context); + default: throw new LogicException('Unsupported object: ' . $this->object); } @@ -330,8 +286,6 @@ private function execute(FunctionalTestCase $test, Context $context) /** * Executes the client operation and return its result. * - * @param Client $client - * @param Context $context Execution context * @return mixed * @throws LogicException if the collection operation is unsupported */ @@ -343,13 +297,16 @@ private function executeForClient(Client $client, Context $context) switch ($this->name) { case 'listDatabaseNames': return iterator_to_array($client->listDatabaseNames($args)); + case 'listDatabases': return $client->listDatabases($args); + case 'watch': return $client->watch( $args['pipeline'] ?? [], array_diff_key($args, ['pipeline' => 1]) ); + default: throw new LogicException('Unsupported client operation: ' . $this->name); } @@ -358,8 +315,6 @@ private function executeForClient(Client $client, Context $context) /** * Executes the collection operation and return its result. * - * @param Collection $collection - * @param Context $context Execution context * @return mixed * @throws LogicException if the collection operation is unsupported */ @@ -374,6 +329,7 @@ private function executeForCollection(Collection $collection, Context $context) $args['pipeline'], array_diff_key($args, ['pipeline' => 1]) ); + case 'bulkWrite': // Merge nested and top-level options (see: SPEC-1158) $options = isset($args['options']) ? (array) $args['options'] : []; @@ -384,16 +340,19 @@ private function executeForCollection(Collection $collection, Context $context) array_map([$this, 'prepareBulkWriteRequest'], $args['requests']), $options ); + case 'createIndex': return $collection->createIndex( $args['keys'], array_diff_key($args, ['keys' => 1]) ); + case 'dropIndex': return $collection->dropIndex( $args['name'], array_diff_key($args, ['name' => 1]) ); + case 'count': case 'countDocuments': case 'find': @@ -401,8 +360,10 @@ private function executeForCollection(Collection $collection, Context $context) $args['filter'] ?? [], array_diff_key($args, ['filter' => 1]) ); + case 'estimatedDocumentCount': return $collection->estimatedDocumentCount($args); + case 'deleteMany': case 'deleteOne': case 'findOneAndDelete': @@ -410,16 +371,20 @@ private function executeForCollection(Collection $collection, Context $context) $args['filter'], array_diff_key($args, ['filter' => 1]) ); + case 'distinct': return $collection->distinct( $args['fieldName'], $args['filter'] ?? [], array_diff_key($args, ['fieldName' => 1, 'filter' => 1]) ); + case 'drop': return $collection->drop($args); + case 'findOne': return $collection->findOne($args['filter'], array_diff_key($args, ['filter' => 1])); + case 'findOneAndReplace': if (isset($args['returnDocument'])) { $args['returnDocument'] = 'after' === strtolower($args['returnDocument']) @@ -427,13 +392,13 @@ private function executeForCollection(Collection $collection, Context $context) : FindOneAndReplace::RETURN_DOCUMENT_BEFORE; } // Fall through - case 'replaceOne': return $collection->{$this->name}( $args['filter'], $args['replacement'], array_diff_key($args, ['filter' => 1, 'replacement' => 1]) ); + case 'findOneAndUpdate': if (isset($args['returnDocument'])) { $args['returnDocument'] = 'after' === strtolower($args['returnDocument']) @@ -441,7 +406,6 @@ private function executeForCollection(Collection $collection, Context $context) : FindOneAndUpdate::RETURN_DOCUMENT_BEFORE; } // Fall through - case 'updateMany': case 'updateOne': return $collection->{$this->name}( @@ -449,6 +413,7 @@ private function executeForCollection(Collection $collection, Context $context) $args['update'], array_diff_key($args, ['filter' => 1, 'update' => 1]) ); + case 'insertMany': // Merge nested and top-level options (see: SPEC-1158) $options = isset($args['options']) ? (array) $args['options'] : []; @@ -458,13 +423,16 @@ private function executeForCollection(Collection $collection, Context $context) $args['documents'], $options ); + case 'insertOne': return $collection->insertOne( $args['document'], array_diff_key($args, ['document' => 1]) ); + case 'listIndexes': return $collection->listIndexes($args); + case 'mapReduce': return $collection->mapReduce( $args['map'], @@ -472,11 +440,13 @@ private function executeForCollection(Collection $collection, Context $context) $args['out'], array_diff_key($args, ['map' => 1, 'reduce' => 1, 'out' => 1]) ); + case 'watch': return $collection->watch( $args['pipeline'] ?? [], array_diff_key($args, ['pipeline' => 1]) ); + default: throw new LogicException('Unsupported collection operation: ' . $this->name); } @@ -485,8 +455,6 @@ private function executeForCollection(Collection $collection, Context $context) /** * Executes the database operation and return its result. * - * @param Database $database - * @param Context $context Execution context * @return mixed * @throws LogicException if the database operation is unsupported */ @@ -501,30 +469,37 @@ private function executeForDatabase(Database $database, Context $context) $args['pipeline'], array_diff_key($args, ['pipeline' => 1]) ); + case 'createCollection': return $database->createCollection( $args['collection'], array_diff_key($args, ['collection' => 1]) ); + case 'dropCollection': return $database->dropCollection( $args['collection'], array_diff_key($args, ['collection' => 1]) ); + case 'listCollectionNames': return iterator_to_array($database->listCollectionNames($args)); + case 'listCollections': return $database->listCollections($args); + case 'runCommand': return $database->command( $args['command'], array_diff_key($args, ['command' => 1]) )->toArray()[0]; + case 'watch': return $database->watch( $args['pipeline'] ?? [], array_diff_key($args, ['pipeline' => 1]) ); + default: throw new LogicException('Unsupported database operation: ' . $this->name); } @@ -533,8 +508,6 @@ private function executeForDatabase(Database $database, Context $context) /** * Executes the GridFS bucket operation and return its result. * - * @param Bucket $bucket - * @param Context $context Execution context * @return mixed * @throws LogicException if the database operation is unsupported */ @@ -553,6 +526,7 @@ private function executeForGridFSBucket(Bucket $bucket, Context $context) } finally { fclose($stream); } + break; case 'download_by_name': @@ -564,6 +538,7 @@ private function executeForGridFSBucket(Bucket $bucket, Context $context) } finally { fclose($stream); } + break; default: @@ -574,9 +549,6 @@ private function executeForGridFSBucket(Bucket $bucket, Context $context) /** * Executes the session operation and return its result. * - * @param Session $session - * @param FunctionalTestCase $test - * @param Context $context Execution context * @return mixed * @throws LogicException if the session operation is unsupported */ @@ -585,19 +557,22 @@ private function executeForSession(Session $session, FunctionalTestCase $test, C switch ($this->name) { case 'abortTransaction': return $session->abortTransaction(); + case 'commitTransaction': return $session->commitTransaction(); + case 'startTransaction': $options = isset($this->arguments['options']) ? (array) $this->arguments['options'] : []; return $session->startTransaction($context->prepareOptions($options)); + case 'withTransaction': /** @var self[] $callbackOperations */ $callbackOperations = array_map(function ($operation) { return self::fromConvenientTransactions($operation); }, $this->arguments['callback']->operations); - $callback = function () use ($callbackOperations, $test, $context) { + $callback = function () use ($callbackOperations, $test, $context): void { foreach ($callbackOperations as $operation) { $operation->assert($test, $context, true); } @@ -606,6 +581,7 @@ private function executeForSession(Session $session, FunctionalTestCase $test, C $options = isset($this->arguments['options']) ? (array) $this->arguments['options'] : []; return with_transaction($session, $callback, $context->prepareOptions($options)); + default: throw new LogicException('Unsupported session operation: ' . $this->name); } @@ -621,16 +597,18 @@ private function executeForTestRunner(FunctionalTestCase $test, Context $context $databaseName = $args['database']; $collectionName = $args['collection']; - $test->assertContains($collectionName, $context->selectDatabase($databaseName)->listCollectionNames()); + $test->assertContains($collectionName, $context->getInternalClient()->selectDatabase($databaseName)->listCollectionNames()); return null; + case 'assertCollectionNotExists': $databaseName = $args['database']; $collectionName = $args['collection']; - $test->assertNotContains($collectionName, $context->selectDatabase($databaseName)->listCollectionNames()); + $test->assertNotContains($collectionName, $context->getInternalClient()->selectDatabase($databaseName)->listCollectionNames()); return null; + case 'assertIndexExists': $databaseName = $args['database']; $collectionName = $args['collection']; @@ -639,6 +617,7 @@ private function executeForTestRunner(FunctionalTestCase $test, Context $context $test->assertContains($indexName, $this->getIndexNames($context, $databaseName, $collectionName)); return null; + case 'assertIndexNotExists': $databaseName = $args['database']; $collectionName = $args['collection']; @@ -647,90 +626,91 @@ private function executeForTestRunner(FunctionalTestCase $test, Context $context $test->assertNotContains($indexName, $this->getIndexNames($context, $databaseName, $collectionName)); return null; + case 'assertSessionPinned': $test->assertInstanceOf(Session::class, $args['session']); $test->assertInstanceOf(Server::class, $args['session']->getServer()); return null; + case 'assertSessionTransactionState': $test->assertInstanceOf(Session::class, $args['session']); $test->assertSame($this->arguments['state'], $args['session']->getTransactionState()); return null; + case 'assertSessionUnpinned': $test->assertInstanceOf(Session::class, $args['session']); $test->assertNull($args['session']->getServer()); return null; + case 'targetedFailPoint': $test->assertInstanceOf(Session::class, $args['session']); $test->configureFailPoint($this->arguments['failPoint'], $args['session']->getServer()); return null; + default: throw new LogicException('Unsupported test runner operation: ' . $this->name); } } - /** - * @param string $databaseName - * @param string $collectionName - * - * @return array - */ - private function getIndexNames(Context $context, $databaseName, $collectionName) + private function getIndexNames(Context $context, string $databaseName, string $collectionName): array { return array_map( function (IndexInfo $indexInfo) { return $indexInfo->getName(); }, - iterator_to_array($context->selectCollection($databaseName, $collectionName)->listIndexes()) + iterator_to_array($context->getInternalClient()->selectCollection($databaseName, $collectionName)->listIndexes()) ); } - /** - * @throws LogicException if the operation object is unsupported - */ + /** @throws LogicException if the operation object is unsupported */ private function getResultAssertionType() { switch ($this->object) { case self::OBJECT_CLIENT: return $this->getResultAssertionTypeForClient(); + case self::OBJECT_COLLECTION: return $this->getResultAssertionTypeForCollection(); + case self::OBJECT_DATABASE: return $this->getResultAssertionTypeForDatabase(); + case self::OBJECT_GRIDFS_BUCKET: return ResultExpectation::ASSERT_SAME; + case self::OBJECT_SESSION0: case self::OBJECT_SESSION1: case self::OBJECT_TEST_RUNNER: return ResultExpectation::ASSERT_NOTHING; + default: throw new LogicException('Unsupported object: ' . $this->object); } } - /** - * @throws LogicException if the collection operation is unsupported - */ + /** @throws LogicException if the collection operation is unsupported */ private function getResultAssertionTypeForClient() { switch ($this->name) { case 'listDatabaseNames': return ResultExpectation::ASSERT_SAME; + case 'listDatabases': return ResultExpectation::ASSERT_SAME_DOCUMENTS; + case 'watch': return ResultExpectation::ASSERT_SAME_DOCUMENTS; + default: throw new LogicException('Unsupported client operation: ' . $this->name); } } - /** - * @throws LogicException if the collection operation is unsupported - */ + /** @throws LogicException if the collection operation is unsupported */ private function getResultAssertionTypeForCollection() { switch ($this->name) { @@ -743,66 +723,83 @@ private function getResultAssertionTypeForCollection() return ResultExpectation::ASSERT_NOTHING; } - return ResultExpectation::ASSERT_SAME_DOCUMENTS; + return ResultExpectation::ASSERT_DOCUMENTS_MATCH; + case 'bulkWrite': return ResultExpectation::ASSERT_BULKWRITE; + case 'count': case 'countDocuments': return ResultExpectation::ASSERT_SAME; + case 'createIndex': case 'dropIndex': return ResultExpectation::ASSERT_MATCHES_DOCUMENT; + case 'distinct': case 'estimatedDocumentCount': return ResultExpectation::ASSERT_SAME; + case 'deleteMany': case 'deleteOne': return ResultExpectation::ASSERT_DELETE; + case 'drop': return ResultExpectation::ASSERT_NOTHING; + case 'findOne': case 'findOneAndDelete': case 'findOneAndReplace': case 'findOneAndUpdate': - return ResultExpectation::ASSERT_SAME_DOCUMENT; + return ResultExpectation::ASSERT_MATCHES_DOCUMENT; + case 'find': - return ResultExpectation::ASSERT_SAME_DOCUMENTS; + return ResultExpectation::ASSERT_DOCUMENTS_MATCH; + case 'insertMany': return ResultExpectation::ASSERT_INSERTMANY; + case 'insertOne': return ResultExpectation::ASSERT_INSERTONE; + case 'listIndexes': return ResultExpectation::ASSERT_SAME_DOCUMENTS; + case 'mapReduce': - return ResultExpectation::ASSERT_SAME_DOCUMENTS; + return ResultExpectation::ASSERT_DOCUMENTS_MATCH; + case 'replaceOne': case 'updateMany': case 'updateOne': return ResultExpectation::ASSERT_UPDATE; + case 'watch': return ResultExpectation::ASSERT_SAME_DOCUMENTS; + default: throw new LogicException('Unsupported collection operation: ' . $this->name); } } - /** - * @throws LogicException if the database operation is unsupported - */ + /** @throws LogicException if the database operation is unsupported */ private function getResultAssertionTypeForDatabase() { switch ($this->name) { case 'aggregate': case 'listCollections': return ResultExpectation::ASSERT_SAME_DOCUMENTS; + case 'listCollectionNames': return ResultExpectation::ASSERT_SAME; + case 'createCollection': case 'dropCollection': case 'runCommand': return ResultExpectation::ASSERT_MATCHES_DOCUMENT; + case 'watch': return ResultExpectation::ASSERT_SAME_DOCUMENTS; + default: throw new LogicException('Unsupported database operation: ' . $this->name); } @@ -811,11 +808,9 @@ private function getResultAssertionTypeForDatabase() /** * Prepares a request element for a bulkWrite operation. * - * @param stdClass $request - * @return array * @throws LogicException if the bulk write request is unsupported */ - private function prepareBulkWriteRequest(stdClass $request) + private function prepareBulkWriteRequest(stdClass $request): array { $args = (array) $request->arguments; @@ -828,8 +823,10 @@ private function prepareBulkWriteRequest(stdClass $request) array_diff_key($args, ['filter' => 1]), ], ]; + case 'insertOne': - return [ 'insertOne' => [ $args['document'] ]]; + return ['insertOne' => [$args['document']]]; + case 'replaceOne': return [ 'replaceOne' => [ @@ -838,6 +835,7 @@ private function prepareBulkWriteRequest(stdClass $request) array_diff_key($args, ['filter' => 1, 'replacement' => 1]), ], ]; + case 'updateMany': case 'updateOne': return [ @@ -847,6 +845,7 @@ private function prepareBulkWriteRequest(stdClass $request) array_diff_key($args, ['filter' => 1, 'update' => 1]), ], ]; + default: throw new LogicException('Unsupported bulk write request: ' . $request->name); } diff --git a/tests/SpecTests/PrimaryStepDownSpecTest.php b/tests/SpecTests/PrimaryStepDownSpecTest.php index 986c1a477..db028d7d9 100644 --- a/tests/SpecTests/PrimaryStepDownSpecTest.php +++ b/tests/SpecTests/PrimaryStepDownSpecTest.php @@ -2,7 +2,6 @@ namespace MongoDB\Tests\SpecTests; -use IteratorIterator; use MongoDB\Client; use MongoDB\Collection; use MongoDB\Driver\Command; @@ -13,21 +12,17 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Operation\BulkWrite; use MongoDB\Tests\CommandObserver; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; use UnexpectedValueException; + use function current; use function sprintf; -/** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests - */ +/** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests */ class PrimaryStepDownSpecTest extends FunctionalTestCase { - use SetUpTearDownTrait; - - const INTERRUPTED_AT_SHUTDOWN = 11600; - const NOT_MASTER = 10107; - const SHUTDOWN_IN_PROGRESS = 91; + public const INTERRUPTED_AT_SHUTDOWN = 11600; + public const NOT_PRIMARY = 10107; + public const SHUTDOWN_IN_PROGRESS = 91; /** @var Client */ private $client; @@ -35,20 +30,18 @@ class PrimaryStepDownSpecTest extends FunctionalTestCase /** @var Collection */ private $collection; - private function doSetUp() + public function setUp(): void { parent::setUp(); - $this->client = new Client(static::getUri(), ['retryWrites' => false, 'heartbeatFrequencyMS' => 500, 'serverSelectionTimeoutMS' => 20000, 'serverSelectionTryOnce' => false]); + $this->client = self::createTestClient(null, ['retryWrites' => false, 'heartbeatFrequencyMS' => 500, 'serverSelectionTimeoutMS' => 20000, 'serverSelectionTryOnce' => false]); $this->dropAndRecreateCollection(); $this->collection = $this->client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); } - /** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#id10 - */ - public function testNotMasterKeepsConnectionPool() + /** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#not-primary-keep-connection-pool */ + public function testNotPrimaryKeepsConnectionPool(): void { $runOn = [(object) ['minServerVersion' => '4.1.11', 'topology' => [self::TOPOLOGY_REPLICASET]]]; $this->checkServerRequirements($runOn); @@ -59,7 +52,7 @@ public function testNotMasterKeepsConnectionPool() 'mode' => ['times' => 1], 'data' => [ 'failCommands' => ['insert'], - 'errorCode' => self::NOT_MASTER, + 'errorCode' => self::NOT_PRIMARY, ], ]); @@ -70,7 +63,7 @@ public function testNotMasterKeepsConnectionPool() $this->insertDocuments(1); } catch (BulkWriteException $e) { // Verify that the insert failed with an operation failure with 10107 code. - $this->assertSame(self::NOT_MASTER, $e->getCode()); + $this->assertSame(self::NOT_PRIMARY, $e->getCode()); } // Execute an insert into the test collection of a {test: 1} document and verify that it succeeds. @@ -81,10 +74,8 @@ public function testNotMasterKeepsConnectionPool() $this->assertSame($totalConnectionsCreated, $this->getTotalConnectionsCreated()); } - /** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#id11 - */ - public function testNotMasterResetConnectionPool() + /** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#not-primary-reset-connection-pool */ + public function testNotPrimaryResetConnectionPool(): void { $runOn = [(object) ['minServerVersion' => '4.0.0', 'maxServerVersion' => '4.0.999', 'topology' => [self::TOPOLOGY_REPLICASET]]]; $this->checkServerRequirements($runOn); @@ -95,7 +86,7 @@ public function testNotMasterResetConnectionPool() 'mode' => ['times' => 1], 'data' => [ 'failCommands' => ['insert'], - 'errorCode' => self::NOT_MASTER, + 'errorCode' => self::NOT_PRIMARY, ], ]); @@ -106,21 +97,22 @@ public function testNotMasterResetConnectionPool() $this->insertDocuments(1); } catch (BulkWriteException $e) { // Verify that the insert failed with an operation failure with 10107 code. - $this->assertSame(self::NOT_MASTER, $e->getCode()); + $this->assertSame(self::NOT_PRIMARY, $e->getCode()); } - // Verify that the connection pool has been cleared - $this->assertSame($totalConnectionsCreated + 1, $this->getTotalConnectionsCreated()); + /* Verify that the connection pool has been cleared and that a new + * connection has been created. Use ">=" to allow for the possibility + * that the server created additional connections unrelated to this + * test. */ + $this->assertGreaterThanOrEqual($totalConnectionsCreated + 1, $this->getTotalConnectionsCreated()); // Execute an insert into the test collection of a {test: 1} document and verify that it succeeds. $result = $this->insertDocuments(1); $this->assertSame(1, $result->getInsertedCount()); } - /** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#id12 - */ - public function testShutdownResetConnectionPool() + /** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#shutdown-in-progress-reset-connection-pool */ + public function testShutdownResetConnectionPool(): void { $runOn = [(object) ['minServerVersion' => '4.0.0']]; $this->checkServerRequirements($runOn); @@ -145,18 +137,19 @@ public function testShutdownResetConnectionPool() $this->assertSame(self::SHUTDOWN_IN_PROGRESS, $e->getCode()); } - // Verify that the connection pool has been cleared - $this->assertSame($totalConnectionsCreated + 1, $this->getTotalConnectionsCreated()); + /* Verify that the connection pool has been cleared and that a new + * connection has been created. Use ">=" to allow for the possibility + * that the server created additional connections unrelated to this + * test. */ + $this->assertGreaterThanOrEqual($totalConnectionsCreated + 1, $this->getTotalConnectionsCreated()); // Execute an insert into the test collection of a {test: 1} document and verify that it succeeds. $result = $this->insertDocuments(1); $this->assertSame(1, $result->getInsertedCount()); } - /** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#id13 - */ - public function testInterruptedAtShutdownResetConnectionPool() + /** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#interrupted-at-shutdown-reset-connection-pool */ + public function testInterruptedAtShutdownResetConnectionPool(): void { $runOn = [(object) ['minServerVersion' => '4.0.0']]; $this->checkServerRequirements($runOn); @@ -181,18 +174,19 @@ public function testInterruptedAtShutdownResetConnectionPool() $this->assertSame(self::INTERRUPTED_AT_SHUTDOWN, $e->getCode()); } - // Verify that the connection pool has been cleared - $this->assertSame($totalConnectionsCreated + 1, $this->getTotalConnectionsCreated()); + /* Verify that the connection pool has been cleared and that a new + * connection has been created. Use ">=" to allow for the possibility + * that the server created additional connections unrelated to this + * test. */ + $this->assertGreaterThanOrEqual($totalConnectionsCreated + 1, $this->getTotalConnectionsCreated()); // Execute an insert into the test collection of a {test: 1} document and verify that it succeeds. $result = $this->insertDocuments(1); $this->assertSame(1, $result->getInsertedCount()); } - /** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#id9 - */ - public function testGetMoreIteration() + /** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#getmore-iteration */ + public function testGetMoreIteration(): void { $this->markTestSkipped('Test causes subsequent failures in other tests (see PHPLIB-471)'); @@ -205,17 +199,16 @@ public function testGetMoreIteration() // Start a find operation on the collection with a batch size of 2, and retrieve the first batch of results. $cursor = $this->collection->find([], ['batchSize' => 2]); - $iterator = new IteratorIterator($cursor); - $iterator->rewind(); - $this->assertTrue($iterator->valid()); + $cursor->rewind(); + $this->assertTrue($cursor->valid()); - $iterator->next(); - $this->assertTrue($iterator->valid()); + $cursor->next(); + $this->assertTrue($cursor->valid()); $totalConnectionsCreated = $this->getTotalConnectionsCreated(); // Send a {replSetStepDown: 5, force: true} command to the current primary and verify that the command succeeded - $primary = $this->client->getManager()->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY)); + $primary = $this->client->getManager()->selectServer(); $success = false; $attempts = 0; @@ -235,14 +228,14 @@ public function testGetMoreIteration() $events = []; $observer = new CommandObserver(); $observer->observe( - function () use ($iterator) { - $iterator->next(); + function () use ($cursor): void { + $cursor->next(); }, - function ($event) use (&$events) { + function ($event) use (&$events): void { $events[] = $event; } ); - $this->assertTrue($iterator->valid()); + $this->assertTrue($cursor->valid()); $this->assertCount(1, $events); $this->assertSame('getMore', $events[0]['started']->getCommandName()); @@ -250,7 +243,7 @@ function ($event) use (&$events) { $this->assertSame($totalConnectionsCreated, $this->getTotalConnectionsCreated($cursor->getServer())); // Wait to allow primary election to complete and prevent subsequent test failures - $this->waitForMasterReelection(); + $this->waitForPrimaryReelection(); } private function insertDocuments($count) @@ -266,20 +259,20 @@ private function insertDocuments($count) return $this->collection->bulkWrite($operations, ['writeConcern' => new WriteConcern('majority')]); } - private function dropAndRecreateCollection() + private function dropAndRecreateCollection(): void { $this->client->selectCollection($this->getDatabaseName(), $this->getCollectionName())->drop(); $this->client->selectDatabase($this->getDatabaseName())->command(['create' => $this->getCollectionName()]); } - private function getTotalConnectionsCreated(Server $server = null) + private function getTotalConnectionsCreated(?Server $server = null) { - $server = $server ?: $this->client->getManager()->selectServer(new ReadPreference('primary')); + $server = $server ?: $this->client->getManager()->selectServer(); $cursor = $server->executeCommand( $this->getDatabaseName(), new Command(['serverStatus' => 1]), - new ReadPreference(ReadPreference::RP_PRIMARY) + new ReadPreference(ReadPreference::PRIMARY) ); $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); @@ -292,14 +285,14 @@ private function getTotalConnectionsCreated(Server $server = null) throw new UnexpectedValueException('Could not determine number of total connections'); } - private function waitForMasterReelection() + private function waitForPrimaryReelection(): void { try { $this->insertDocuments(1); return; } catch (DriverException $e) { - $this->client->getManager()->selectServer(new ReadPreference('primary')); + $this->client->getManager()->selectServer(); return; } diff --git a/tests/SpecTests/ReadWriteConcernSpecTest.php b/tests/SpecTests/ReadWriteConcernSpecTest.php index 12f4f624f..ae5ec27f1 100644 --- a/tests/SpecTests/ReadWriteConcernSpecTest.php +++ b/tests/SpecTests/ReadWriteConcernSpecTest.php @@ -3,14 +3,13 @@ namespace MongoDB\Tests\SpecTests; use stdClass; + use function basename; use function dirname; use function file_get_contents; use function glob; -/** - * @see https://github.com/mongodb/specifications/tree/master/source/read-write-concern - */ +/** @see https://github.com/mongodb/specifications/tree/master/source/read-write-concern */ class ReadWriteConcernSpecTest extends FunctionalTestCase { /** @var array */ @@ -22,7 +21,7 @@ class ReadWriteConcernSpecTest extends FunctionalTestCase * @param stdClass $expected Expected command document * @param stdClass $actual Actual command document */ - public static function assertCommandMatches(stdClass $expected, stdClass $actual) + public static function assertCommandMatches(stdClass $expected, stdClass $actual): void { foreach ($expected as $key => $value) { if ($value === null) { @@ -44,7 +43,7 @@ public static function assertCommandMatches(stdClass $expected, stdClass $actual * @param string $databaseName Name of database under test * @param string $collectionName Name of collection under test */ - public function testReadWriteConcern(stdClass $test, array $runOn = null, array $data, $databaseName = null, $collectionName = null) + public function testReadWriteConcern(stdClass $test, ?array $runOn, array $data, ?string $databaseName = null, ?string $collectionName = null): void { if (isset(self::$incompleteTests[$this->dataDescription()])) { $this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]); @@ -72,7 +71,7 @@ public function testReadWriteConcern(stdClass $test, array $runOn = null, array } if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromReadWriteConcern($test->expectations); + $commandExpectations = CommandExpectations::fromReadWriteConcern($context->getClient(), $test->expectations); $commandExpectations->startMonitoring(); } @@ -99,8 +98,10 @@ public function provideTests() $group = basename(dirname($filename)) . '/' . basename($filename, '.json'); $runOn = $json->runOn ?? null; $data = $json->data ?? []; + // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps $databaseName = $json->database_name ?? null; $collectionName = $json->collection_name ?? null; + // phpcs:enable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps foreach ($json->tests as $test) { $name = $group . ': ' . $test->description; diff --git a/tests/SpecTests/ResultExpectation.php b/tests/SpecTests/ResultExpectation.php index 199fc0035..9626719e2 100644 --- a/tests/SpecTests/ResultExpectation.php +++ b/tests/SpecTests/ResultExpectation.php @@ -11,6 +11,7 @@ use MongoDB\InsertOneResult; use MongoDB\UpdateResult; use stdClass; + use function call_user_func; use function is_array; use function is_object; @@ -21,19 +22,19 @@ */ final class ResultExpectation { - const ASSERT_NOTHING = 0; - const ASSERT_BULKWRITE = 1; - const ASSERT_DELETE = 2; - const ASSERT_INSERTMANY = 3; - const ASSERT_INSERTONE = 4; - const ASSERT_UPDATE = 5; - const ASSERT_SAME = 6; - const ASSERT_SAME_DOCUMENT = 7; - const ASSERT_SAME_DOCUMENTS = 8; - const ASSERT_MATCHES_DOCUMENT = 9; - const ASSERT_NULL = 10; - const ASSERT_CALLABLE = 11; - const ASSERT_DOCUMENTS_MATCH = 12; + public const ASSERT_NOTHING = 0; + public const ASSERT_BULKWRITE = 1; + public const ASSERT_DELETE = 2; + public const ASSERT_INSERTMANY = 3; + public const ASSERT_INSERTONE = 4; + public const ASSERT_UPDATE = 5; + public const ASSERT_SAME = 6; + public const ASSERT_SAME_DOCUMENT = 7; + public const ASSERT_SAME_DOCUMENTS = 8; + public const ASSERT_MATCHES_DOCUMENT = 9; + public const ASSERT_NULL = 10; + public const ASSERT_CALLABLE = 11; + public const ASSERT_DOCUMENTS_MATCH = 12; /** @var integer */ private $assertionType = self::ASSERT_NOTHING; @@ -44,11 +45,7 @@ final class ResultExpectation /** @var callable */ private $assertionCallable; - /** - * @param integer $assertionType - * @param mixed $expectedValue - */ - private function __construct($assertionType, $expectedValue) + private function __construct(int $assertionType, $expectedValue) { switch ($assertionType) { case self::ASSERT_BULKWRITE: @@ -59,12 +56,14 @@ private function __construct($assertionType, $expectedValue) if (! is_object($expectedValue)) { throw InvalidArgumentException::invalidType('$expectedValue', $expectedValue, 'object'); } + break; case self::ASSERT_SAME_DOCUMENTS: if (! self::isArrayOfObjects($expectedValue)) { throw InvalidArgumentException::invalidType('$expectedValue', $expectedValue, 'object[]'); } + break; } @@ -72,19 +71,6 @@ private function __construct($assertionType, $expectedValue) $this->expectedValue = $expectedValue; } - public static function fromChangeStreams(stdClass $result, callable $assertionCallable) - { - if (! property_exists($result, 'success')) { - return new self(self::ASSERT_NOTHING, null); - } - - $o = new self(self::ASSERT_CALLABLE, $result->success); - - $o->assertionCallable = $assertionCallable; - - return $o; - } - public static function fromClientSideEncryption(stdClass $operation, $defaultAssertionType) { if (property_exists($operation, 'result') && ! self::isErrorResult($operation->result)) { @@ -170,7 +156,7 @@ public static function fromTransactions(stdClass $operation, $defaultAssertionTy * @param mixed $result Result (if any) from the actual outcome * @throws LogicException if the assertion type is unsupported */ - public function assert(FunctionalTestCase $test, $actual) + public function assert(FunctionalTestCase $test, $actual): void { $expected = $this->expectedValue; @@ -216,6 +202,7 @@ public function assert(FunctionalTestCase $test, $actual) if (isset($expected->upsertedIds)) { $test->assertSameDocument($expected->upsertedIds, $actual->getUpsertedIds()); } + break; case self::ASSERT_CALLABLE: @@ -228,6 +215,7 @@ public function assert(FunctionalTestCase $test, $actual) if (isset($expected->deletedCount)) { $test->assertSame($expected->deletedCount, $actual->getDeletedCount()); } + break; case self::ASSERT_INSERTMANY: @@ -247,6 +235,7 @@ public function assert(FunctionalTestCase $test, $actual) if (isset($expected->insertedIds) && $actual instanceof BulkWriteResult) { $test->assertSameDocument($expected->insertedIds, $actual->getInsertedIds()); } + break; case self::ASSERT_INSERTONE: @@ -265,6 +254,7 @@ public function assert(FunctionalTestCase $test, $actual) ['insertedId' => $actual->getInsertedId()] ); } + break; case self::ASSERT_MATCHES_DOCUMENT: @@ -325,6 +315,7 @@ public function assert(FunctionalTestCase $test, $actual) ['upsertedId' => $actual->getUpsertedId()] ); } + break; default: @@ -357,9 +348,8 @@ private static function isArrayOfObjects($array) * * @see https://github.com/mongodb/specifications/blob/master/source/transactions/tests/README.rst#test-format * @param mixed $result - * @return boolean */ - private static function isErrorResult($result) + private static function isErrorResult($result): bool { if (! is_object($result)) { return false; diff --git a/tests/SpecTests/RetryableReadsSpecTest.php b/tests/SpecTests/RetryableReadsSpecTest.php index befe361bc..37465aff9 100644 --- a/tests/SpecTests/RetryableReadsSpecTest.php +++ b/tests/SpecTests/RetryableReadsSpecTest.php @@ -3,6 +3,7 @@ namespace MongoDB\Tests\SpecTests; use stdClass; + use function basename; use function file_get_contents; use function glob; @@ -13,6 +14,7 @@ * Retryable reads spec tests. * * @see https://github.com/mongodb/specifications/tree/master/source/retryable-reads + * @group serverless */ class RetryableReadsSpecTest extends FunctionalTestCase { @@ -23,13 +25,16 @@ class RetryableReadsSpecTest extends FunctionalTestCase 'listIndexNames' => 'Not implemented', ]; + /** @var array */ + private static $incompleteTests = []; + /** * Assert that the expected and actual command documents match. * * @param stdClass $expected Expected command document * @param stdClass $actual Actual command document */ - public static function assertCommandMatches(stdClass $expected, stdClass $actual) + public static function assertCommandMatches(stdClass $expected, stdClass $actual): void { static::assertDocumentsMatch($expected, $actual); } @@ -37,15 +42,18 @@ public static function assertCommandMatches(stdClass $expected, stdClass $actual /** * Execute an individual test case from the specification. * - * @dataProvider provideTests * @param stdClass $test Individual "tests[]" document * @param array $runOn Top-level "runOn" array with server requirements * @param array|object $data Top-level "data" array to initialize collection * @param string $databaseName Name of database under test * @param string|null $collectionName Name of collection under test * @param string|null $bucketName Name of GridFS bucket under test + * + * @dataProvider provideTests + * @group matrix-testing-exclude-server-4.4-driver-4.2 + * @group matrix-testing-exclude-server-5.0-driver-4.2 */ - public function testRetryableReads(stdClass $test, array $runOn = null, $data, $databaseName, $collectionName, $bucketName) + public function testRetryableReads(stdClass $test, ?array $runOn, $data, string $databaseName, ?string $collectionName, ?string $bucketName): void { if (isset($runOn)) { $this->checkServerRequirements($runOn); @@ -61,6 +69,10 @@ public function testRetryableReads(stdClass $test, array $runOn = null, $data, $ $this->skipIfChangeStreamIsNotSupported(); } + if (isset(self::$incompleteTests[$this->dataDescription()])) { + $this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]); + } + $context = Context::fromRetryableReads($test, $databaseName, $collectionName, $bucketName); $this->setContext($context); @@ -80,7 +92,7 @@ public function testRetryableReads(stdClass $test, array $runOn = null, $data, $ } if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromRetryableReads($test->expectations); + $commandExpectations = CommandExpectations::fromRetryableReads($context->getClient(), $test->expectations); $commandExpectations->startMonitoring(); } @@ -107,9 +119,11 @@ public function provideTests() $group = basename($filename, '.json'); $runOn = $json->runOn ?? null; $data = $json->data ?? []; + // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps $databaseName = $json->database_name ?? null; $collectionName = $json->collection_name ?? null; $bucketName = $json->bucket_name ?? null; + // phpcs:enable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps foreach ($json->tests as $test) { $name = $group . ': ' . $test->description; diff --git a/tests/SpecTests/RetryableWritesSpecTest.php b/tests/SpecTests/RetryableWritesSpecTest.php index 5d41a752f..a0506cadc 100644 --- a/tests/SpecTests/RetryableWritesSpecTest.php +++ b/tests/SpecTests/RetryableWritesSpecTest.php @@ -2,7 +2,13 @@ namespace MongoDB\Tests\SpecTests; +use MongoDB\Driver\Exception\BulkWriteException; +use MongoDB\Driver\Monitoring\CommandFailedEvent; +use MongoDB\Driver\Monitoring\CommandStartedEvent; +use MongoDB\Driver\Monitoring\CommandSubscriber; +use MongoDB\Driver\Monitoring\CommandSucceededEvent; use stdClass; + use function basename; use function file_get_contents; use function glob; @@ -11,9 +17,13 @@ * Retryable writes spec tests. * * @see https://github.com/mongodb/specifications/tree/master/source/retryable-writes + * @group serverless */ class RetryableWritesSpecTest extends FunctionalTestCase { + public const NOT_PRIMARY = 10107; + public const SHUTDOWN_IN_PROGRESS = 91; + /** * Execute an individual test case from the specification. * @@ -22,13 +32,13 @@ class RetryableWritesSpecTest extends FunctionalTestCase * @param array $runOn Top-level "runOn" array with server requirements * @param array $data Top-level "data" array to initialize collection */ - public function testRetryableWrites(stdClass $test, array $runOn = null, array $data) + public function testRetryableWrites(stdClass $test, ?array $runOn, array $data): void { if ($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets()) { $this->markTestSkipped('Transaction numbers are only allowed on a replica set member or mongos (PHPC-1415)'); } - $useMultipleMongoses = isset($test->useMultipleMongoses) && $test->useMultipleMongoses && $this->isShardedCluster(); + $useMultipleMongoses = isset($test->useMultipleMongoses) && $test->useMultipleMongoses && $this->isMongos(); if (isset($runOn)) { $this->checkServerRequirements($runOn); @@ -69,4 +79,84 @@ public function provideTests() return $testArgs; } + + /** + * Prose test 1: when encountering a NoWritesPerformed error after an error with a RetryableWriteError label + */ + public function testNoWritesPerformedErrorReturnsOriginalError(): void + { + if (! $this->isReplicaSet()) { + $this->markTestSkipped('Test only applies to replica sets'); + } + + $this->skipIfServerVersion('<', '4.4.0', 'NoWritesPerformed error label is only supported on MongoDB 4.4+'); + + $client = self::createTestClient(null, ['retryWrites' => true]); + + // Step 2: Configure a fail point with error code 91 + $this->configureFailPoint([ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'writeConcernError' => [ + 'code' => self::SHUTDOWN_IN_PROGRESS, + 'errorLabels' => ['RetryableWriteError'], + ], + 'failCommands' => ['insert'], + ], + ]); + + $subscriber = new class ($this) implements CommandSubscriber { + private $testCase; + + public function __construct(FunctionalTestCase $testCase) + { + $this->testCase = $testCase; + } + + public function commandStarted(CommandStartedEvent $event): void + { + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + if ($event->getCommandName() === 'insert') { + // Step 3: Configure a fail point with code 10107 + $this->testCase->configureFailPoint([ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'errorCode' => RetryableWritesSpecTest::NOT_PRIMARY, + 'errorLabels' => ['RetryableWriteError', 'NoWritesPerformed'], + 'failCommands' => ['insert'], + ], + ]); + } + } + + public function commandFailed(CommandFailedEvent $event): void + { + } + }; + + $client->getManager()->addSubscriber($subscriber); + + // Step 4: Run insertOne + try { + $client->selectCollection('db', 'retryable_writes')->insertOne(['write' => 1]); + } catch (BulkWriteException $e) { + $writeConcernError = $e->getWriteResult()->getWriteConcernError(); + $this->assertNotNull($writeConcernError); + + // Assert that the write concern error is from the first failpoint + $this->assertSame(self::SHUTDOWN_IN_PROGRESS, $writeConcernError->getCode()); + } + + // Step 5: Disable the fail point + $client->getManager()->removeSubscriber($subscriber); + $this->configureFailPoint([ + 'configureFailPoint' => 'failCommand', + 'mode' => 'off', + ]); + } } diff --git a/tests/SpecTests/TransactionsSpecTest.php b/tests/SpecTests/TransactionsSpecTest.php index 2bec5ab41..e88fee203 100644 --- a/tests/SpecTests/TransactionsSpecTest.php +++ b/tests/SpecTests/TransactionsSpecTest.php @@ -4,14 +4,11 @@ use MongoDB\BSON\Int64; use MongoDB\BSON\Timestamp; -use MongoDB\Client; use MongoDB\Driver\Command; use MongoDB\Driver\Exception\ServerException; -use MongoDB\Driver\Manager; -use MongoDB\Driver\ReadPreference; use MongoDB\Driver\Server; use stdClass; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; + use function array_unique; use function basename; use function count; @@ -27,9 +24,7 @@ */ class TransactionsSpecTest extends FunctionalTestCase { - use SetUpTearDownTrait; - - const INTERRUPTED = 11601; + public const INTERRUPTED = 11601; /** * In addition to the useMultipleMongoses tests, these should all pass @@ -43,7 +38,7 @@ class TransactionsSpecTest extends FunctionalTestCase 'transactions/pin-mongos: unpin after transient error within a transaction and commit' => 'isMaster failpoints cannot be disabled', ]; - private function doSetUp() + public function setUp(): void { parent::setUp(); @@ -52,7 +47,7 @@ private function doSetUp() $this->skipIfTransactionsAreNotSupported(); } - private function doTearDown() + public function tearDown(): void { if ($this->hasFailed()) { static::killAllSessions(); @@ -66,10 +61,11 @@ private function doTearDown() * * Note: this method may modify the $expected object. * + * @see https://github.com/mongodb/specifications/blob/master/source/transactions/tests/README.rst#command-started-events * @param stdClass $expected Expected command document * @param stdClass $actual Actual command document */ - public static function assertCommandMatches(stdClass $expected, stdClass $actual) + public static function assertCommandMatches(stdClass $expected, stdClass $actual): void { if (isset($expected->getMore) && $expected->getMore === 42) { static::assertObjectHasAttribute('getMore', $actual); @@ -104,33 +100,52 @@ public static function assertCommandMatches(stdClass $expected, stdClass $actual * preferable to skipping the txnNumber assertion. */ //unset($expected['txnNumber']); - foreach ($expected as $key => $value) { - if ($value === null) { - static::assertObjectNotHasAttribute($key, $actual); - unset($expected->{$key}); - } - } + static::assertCommandOmittedFields($expected, $actual); static::assertDocumentsMatch($expected, $actual); } + /** + * @dataProvider provideTransactionsTests + * @group serverless + */ + public function testTransactions(stdClass $test, ?array $runOn, array $data, ?string $databaseName = null, ?string $collectionName = null): void + { + $this->runTransactionTest($test, $runOn, $data, $databaseName, $collectionName); + } + + public function provideTransactionsTests(): array + { + return $this->provideTests('transactions'); + } + + /** @dataProvider provideTransactionsConvenientApiTests */ + public function testTransactionsConvenientApi(stdClass $test, ?array $runOn, array $data, ?string $databaseName = null, ?string $collectionName = null): void + { + $this->runTransactionTest($test, $runOn, $data, $databaseName, $collectionName); + } + + public function provideTransactionsConvenientApiTests(): array + { + return $this->provideTests('transactions-convenient-api'); + } + /** * Execute an individual test case from the specification. * - * @dataProvider provideTests * @param stdClass $test Individual "tests[]" document * @param array $runOn Top-level "runOn" array with server requirements * @param array $data Top-level "data" array to initialize collection * @param string $databaseName Name of database under test * @param string $collectionName Name of collection under test */ - public function testTransactions(stdClass $test, array $runOn = null, array $data, $databaseName = null, $collectionName = null) + private function runTransactionTest(stdClass $test, ?array $runOn, array $data, ?string $databaseName = null, ?string $collectionName = null): void { if (isset(self::$incompleteTests[$this->dataDescription()])) { $this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]); } - $useMultipleMongoses = isset($test->useMultipleMongoses) && $test->useMultipleMongoses && $this->isShardedCluster(); + $useMultipleMongoses = isset($test->useMultipleMongoses) && $test->useMultipleMongoses && $this->isMongos(); if (isset($runOn)) { $this->checkServerRequirements($runOn); @@ -156,7 +171,7 @@ public function testTransactions(stdClass $test, array $runOn = null, array $dat } if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromTransactions($test->expectations); + $commandExpectations = CommandExpectations::fromTransactions($context->getClient(), $test->expectations); $commandExpectations->startMonitoring(); } @@ -177,17 +192,19 @@ public function testTransactions(stdClass $test, array $runOn = null, array $dat } } - public function provideTests() + private function provideTests(string $dir): array { $testArgs = []; - foreach (glob(__DIR__ . '/transactions*/*.json') as $filename) { + foreach (glob(__DIR__ . '/' . $dir . '/*.json') as $filename) { $json = $this->decodeJson(file_get_contents($filename)); $group = basename(dirname($filename)) . '/' . basename($filename, '.json'); $runOn = $json->runOn ?? null; $data = $json->data ?? []; + // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps $databaseName = $json->database_name ?? null; $collectionName = $json->collection_name ?? null; + // phpcs:enable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps foreach ($json->tests as $test) { $name = $group . ': ' . $test->description; @@ -203,13 +220,15 @@ public function provideTests() * ClientSession unpins the session and normal server selection is performed * for the next operation. */ - public function testStartingNewTransactionOnPinnedSessionUnpinsSession() + public function testStartingNewTransactionOnPinnedSessionUnpinsSession(): void { - if (! $this->isShardedClusterUsingReplicasets()) { - $this->markTestSkipped('Mongos pinning tests can only run on sharded clusters using replica sets'); + $this->skipIfTransactionsAreNotSupported(); + + if (! $this->isMongos()) { + $this->markTestSkipped('Pinning tests require mongos'); } - $client = new Client($this->getUri(true)); + $client = self::createTestClient(static::getUri(true)); $session = $client->startSession(); $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); @@ -241,13 +260,15 @@ public function testStartingNewTransactionOnPinnedSessionUnpinsSession() * ClientSession unpins the session and normal server selection is * performed. */ - public function testRunningNonTransactionOperationOnPinnedSessionUnpinsSession() + public function testRunningNonTransactionOperationOnPinnedSessionUnpinsSession(): void { - if (! $this->isShardedClusterUsingReplicasets()) { - $this->markTestSkipped('Mongos pinning tests can only run on sharded clusters using replica sets'); + $this->skipIfTransactionsAreNotSupported(); + + if (! $this->isMongos()) { + $this->markTestSkipped('Pinning tests require mongos'); } - $client = new Client($this->getUri(true)); + $client = self::createTestClient(static::getUri(true)); $session = $client->startSession(); $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); @@ -275,7 +296,7 @@ public function testRunningNonTransactionOperationOnPinnedSessionUnpinsSession() /** * Create the collection, since it cannot be created within a transaction. */ - protected function createTestCollection() + protected function createTestCollection(): void { $context = $this->getContext(); @@ -290,10 +311,15 @@ protected function createTestCollection() * previously failed test. For sharded clusters, this command will be run * on all mongos nodes. */ - private static function killAllSessions() + private static function killAllSessions(): void { - $manager = new Manager(static::getUri()); - $primary = $manager->selectServer(new ReadPreference('primary')); + // killAllSessions is not supported on serverless, see CLOUDP-84298 + if (static::isServerless()) { + return; + } + + $manager = static::createTestManager(); + $primary = $manager->selectServer(); $servers = $primary->getType() === Server::TYPE_MONGOS ? $manager->getServers() @@ -305,6 +331,7 @@ private static function killAllSessions() if (! isset($server->getInfo()['logicalSessionTimeoutMinutes'])) { continue; } + $server->executeCommand('admin', new Command(['killAllSessions' => []])); } catch (ServerException $e) { // Interrupted error is safe to ignore (see: SERVER-38335) @@ -318,10 +345,9 @@ private static function killAllSessions() /** * Work around potential error executing distinct on sharded clusters. * - * @param array $operations - * @see https://github.com/mongodb/specifications/tree/master/source/transactions/tests#why-do-tests-that-run-distinct-sometimes-fail-with-staledbversionts. + * @see https://github.com/mongodb/specifications/tree/master/source/transactions/tests#why-do-tests-that-run-distinct-sometimes-fail-with-staledbversion */ - private function preventStaleDbVersionError(array $operations) + private function preventStaleDbVersionError(array $operations): void { if (! $this->isShardedCluster()) { return; diff --git a/tests/SpecTests/atlas_data_lake/estimatedDocumentCount.json b/tests/SpecTests/atlas_data_lake/estimatedDocumentCount.json index d039a51f0..997a3ab3f 100644 --- a/tests/SpecTests/atlas_data_lake/estimatedDocumentCount.json +++ b/tests/SpecTests/atlas_data_lake/estimatedDocumentCount.json @@ -16,7 +16,9 @@ "command_started_event": { "command": { "count": "driverdata" - } + }, + "command_name": "count", + "database_name": "test" } } ] diff --git a/tests/SpecTests/atlas_data_lake/getMore.json b/tests/SpecTests/atlas_data_lake/getMore.json index fa1deab4f..e2e1d4788 100644 --- a/tests/SpecTests/atlas_data_lake/getMore.json +++ b/tests/SpecTests/atlas_data_lake/getMore.json @@ -54,4 +54,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/tests/SpecTests/atlas_data_lake/listCollections.json b/tests/SpecTests/atlas_data_lake/listCollections.json index 8d8a8f6c1..e419f7b3e 100644 --- a/tests/SpecTests/atlas_data_lake/listCollections.json +++ b/tests/SpecTests/atlas_data_lake/listCollections.json @@ -22,4 +22,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/tests/SpecTests/atlas_data_lake/listDatabases.json b/tests/SpecTests/atlas_data_lake/listDatabases.json index f8ec9a0bf..6458148e4 100644 --- a/tests/SpecTests/atlas_data_lake/listDatabases.json +++ b/tests/SpecTests/atlas_data_lake/listDatabases.json @@ -21,4 +21,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/tests/SpecTests/atlas_data_lake/runCommand.json b/tests/SpecTests/atlas_data_lake/runCommand.json index f72e863ba..d81ff1a64 100644 --- a/tests/SpecTests/atlas_data_lake/runCommand.json +++ b/tests/SpecTests/atlas_data_lake/runCommand.json @@ -28,4 +28,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/tests/SpecTests/change-streams/README.rst b/tests/SpecTests/change-streams/README.rst deleted file mode 100644 index 8472d1f6c..000000000 --- a/tests/SpecTests/change-streams/README.rst +++ /dev/null @@ -1,203 +0,0 @@ -.. role:: javascript(code) - :language: javascript - -============== -Change Streams -============== - -.. contents:: - --------- - -Introduction -============ - -The YAML and JSON files in this directory are platform-independent tests that -drivers can use to prove their conformance to the Change Streams Spec. - -Several prose tests, which are not easily expressed in YAML, are also presented -in this file. Those tests will need to be manually implemented by each driver. - -Spec Test Format -================ - -Each YAML file has the following keys: - -- ``database_name``: The default database -- ``collection_name``: The default collection -- ``database2_name``: Another database -- ``collection2_name``: Another collection -- ``tests``: An array of tests that are to be run independently of each other. - Each test will have some of the following fields: - - - ``description``: The name of the test. - - ``minServerVersion``: The minimum server version to run this test against. If not present, assume there is no minimum server version. - - ``maxServerVersion``: Reserved for later use - - ``failPoint``(optional): The configureFailPoint command document to run to configure a fail point on the primary server. - - ``target``: The entity on which to run the change stream. Valid values are: - - - ``collection``: Watch changes on collection ``database_name.collection_name`` - - ``database``: Watch changes on database ``database_name`` - - ``client``: Watch changes on entire clusters - - ``topology``: An array of server topologies against which to run the test. - Valid topologies are ``single``, ``replicaset``, and ``sharded``. - - ``changeStreamPipeline``: An array of additional aggregation pipeline stages to add to the change stream - - ``changeStreamOptions``: Additional options to add to the changeStream - - ``operations``: Array of documents, each describing an operation. Each document has the following fields: - - - ``database``: Database against which to run the operation - - ``collection``: Collection against which to run the operation - - ``name``: Name of the command to run - - ``arguments`` (optional): Object of arguments for the command (ex: document to insert) - - - ``expectations``: Optional list of command-started events in Extended JSON format - - ``result``: Document with ONE of the following fields: - - - ``error``: Describes an error received during the test - - ``success``: An Extended JSON array of documents expected to be received from the changeStream - -Spec Test Match Function -======================== - -The definition of MATCH or MATCHES in the Spec Test Runner is as follows: - -- MATCH takes two values, ``expected`` and ``actual`` -- Notation is "Assert [actual] MATCHES [expected] -- Assertion passes if ``expected`` is a subset of ``actual``, with the values ``42`` and ``"42"`` acting as placeholders for "any value" - -Pseudocode implementation of ``actual`` MATCHES ``expected``: - -:: - - If expected is "42" or 42: - Assert that actual exists (is not null or undefined) - Else: - Assert that actual is of the same JSON type as expected - If expected is a JSON array: - For every idx/value in expected: - Assert that actual[idx] MATCHES value - Else if expected is a JSON object: - For every key/value in expected - Assert that actual[key] MATCHES value - Else: - Assert that expected equals actual - -The expected values for ``result.success`` and ``expectations`` are written in Extended JSON. Drivers may adopt any of the following approaches to comparisons, as long as they are consistent: - -- Convert ``actual`` to Extended JSON and compare to ``expected`` -- Convert ``expected`` and ``actual`` to BSON, and compare them -- Convert ``expected`` and ``actual`` to native equivalents of JSON, and compare them - -Spec Test Runner -================ - -Before running the tests - -- Create a MongoClient ``globalClient``, and connect to the server - -For each YAML file, for each element in ``tests``: - -- If ``topology`` does not include the topology of the server instance(s), skip this test. -- Use ``globalClient`` to - - - Drop the database ``database_name`` - - Drop the database ``database2_name`` - - Create the database ``database_name`` and the collection ``database_name.collection_name`` - - Create the database ``database2_name`` and the collection ``database2_name.collection2_name`` - - If the the ``failPoint`` field is present, configure the fail point on the primary server. See - `Server Fail Point <../../transactions/tests#server-fail-point>`_ in the - Transactions spec test documentation for more information. - -- Create a new MongoClient ``client`` -- Begin monitoring all APM events for ``client``. (If the driver uses global listeners, filter out all events that do not originate with ``client``). Filter out any "internal" commands (e.g. ``isMaster``) -- Using ``client``, create a changeStream ``changeStream`` against the specified ``target``. Use ``changeStreamPipeline`` and ``changeStreamOptions`` if they are non-empty -- Using ``globalClient``, run every operation in ``operations`` in serial against the server -- Wait until either: - - - An error occurs - - All operations have been successful AND the changeStream has received as many changes as there are in ``result.success`` - -- Close ``changeStream`` -- If there was an error: - - - Assert that an error was expected for the test. - - Assert that the error MATCHES ``result.error`` - -- Else: - - - Assert that no error was expected for the test - - Assert that the changes received from ``changeStream`` MATCH the results in ``result.success`` - -- If there are any ``expectations`` - - - For each (``expected``, ``idx``) in ``expectations`` - - - Assert that ``actual[idx]`` MATCHES ``expected`` - -- Close the MongoClient ``client`` - -After running all tests - -- Close the MongoClient ``globalClient`` -- Drop database ``database_name`` -- Drop database ``database2_name`` - - -Prose Tests -=========== - -The following tests have not yet been automated, but MUST still be tested - -#. ``ChangeStream`` must continuously track the last seen ``resumeToken`` -#. ``ChangeStream`` will throw an exception if the server response is missing the resume token (if wire version is < 8, this is a driver-side error; for 8+, this is a server-side error) -#. ``ChangeStream`` will automatically resume one time on a resumable error (including `not master`) with the initial pipeline and options, except for the addition/update of a ``resumeToken``. -#. ``ChangeStream`` will not attempt to resume on any error encountered while executing an ``aggregate`` command. -#. ``ChangeStream`` will not attempt to resume after encountering error code 11601 (Interrupted), 136 (CappedPositionLost), or 237 (CursorKilled) while executing a ``getMore`` command. -#. ``ChangeStream`` will perform server selection before attempting to resume, using initial ``readPreference`` -#. Ensure that a cursor returned from an aggregate command with a cursor id and an initial empty batch is not closed on the driver side. -#. The ``killCursors`` command sent during the "Resume Process" must not be allowed to throw an exception. -#. ``$changeStream`` stage for ``ChangeStream`` against a server ``>=4.0`` and ``<4.0.7`` that has not received any results yet MUST include a ``startAtOperationTime`` option when resuming a changestream. -#. ``ChangeStream`` will resume after a ``killCursors`` command is issued for its child cursor. -#. - For a ``ChangeStream`` under these conditions: - - Running against a server ``>=4.0.7``. - - The batch is empty or has been iterated to the last document. - - Expected result: - - ``getResumeToken`` must return the ``postBatchResumeToken`` from the current command response. -#. - For a ``ChangeStream`` under these conditions: - - Running against a server ``<4.0.7``. - - The batch is empty or has been iterated to the last document. - - Expected result: - - ``getResumeToken`` must return the ``_id`` of the last document returned if one exists. - - ``getResumeToken`` must return ``startAfter`` from the initial aggregate if the option was specified. - - ``getResumeToken`` must return ``resumeAfter`` from the initial aggregate if the option was specified. - - If neither the ``startAfter`` nor ``resumeAfter`` options were specified, the ``getResumeToken`` result must be empty. -#. - For a ``ChangeStream`` under these conditions: - - The batch is not empty. - - The batch has been iterated up to but not including the last element. - - Expected result: - - ``getResumeToken`` must return the ``_id`` of the previous document returned. -#. - For a ``ChangeStream`` under these conditions: - - The batch is not empty. - - The batch hasn’t been iterated at all. - - Only the initial ``aggregate`` command has been executed. - - Expected result: - - ``getResumeToken`` must return ``startAfter`` from the initial aggregate if the option was specified. - - ``getResumeToken`` must return ``resumeAfter`` from the initial aggregate if the option was specified. - - If neither the ``startAfter`` nor ``resumeAfter`` options were specified, the ``getResumeToken`` result must be empty. -#. - For a ``ChangeStream`` under these conditions: - - Running against a server ``>=4.0.7``. - - The batch is not empty. - - The batch hasn’t been iterated at all. - - The stream has iterated beyond a previous batch and a ``getMore`` command has just been executed. - - Expected result: - - ``getResumeToken`` must return the ``postBatchResumeToken`` from the previous command response. -#. - For a ``ChangeStream`` under these conditions: - - Running against a server ``<4.0.7``. - - The batch is not empty. - - The batch hasn’t been iterated at all. - - The stream has iterated beyond a previous batch and a ``getMore`` command has just been executed. - - Expected result: - - ``getResumeToken`` must return the ``_id`` of the previous document returned if one exists. - - ``getResumeToken`` must return ``startAfter`` from the initial aggregate if the option was specified. - - ``getResumeToken`` must return ``resumeAfter`` from the initial aggregate if the option was specified. - - If neither the ``startAfter`` nor ``resumeAfter`` options were specified, the ``getResumeToken`` result must be empty. \ No newline at end of file diff --git a/tests/SpecTests/change-streams/change-streams-errors.json b/tests/SpecTests/change-streams/change-streams-errors.json deleted file mode 100644 index f0ae14ebc..000000000 --- a/tests/SpecTests/change-streams/change-streams-errors.json +++ /dev/null @@ -1,151 +0,0 @@ -{ - "collection_name": "test", - "database_name": "change-stream-tests", - "collection2_name": "test2", - "database2_name": "change-stream-tests-2", - "tests": [ - { - "description": "The watch helper must not throw a custom exception when executed against a single server topology, but instead depend on a server error", - "minServerVersion": "3.6.0", - "target": "collection", - "topology": [ - "single" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [], - "expectations": null, - "result": { - "error": { - "code": 40573 - } - } - }, - { - "description": "Change Stream should error when an invalid aggregation stage is passed in", - "minServerVersion": "3.6.0", - "target": "collection", - "topology": [ - "replicaset" - ], - "changeStreamPipeline": [ - { - "$unsupported": "foo" - } - ], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "z": 3 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - }, - { - "$unsupported": "foo" - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "error": { - "code": 40324 - } - } - }, - { - "description": "Change Stream should error when _id is projected out", - "minServerVersion": "4.1.11", - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [ - { - "$project": { - "_id": 0 - } - } - ], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "z": 3 - } - } - } - ], - "result": { - "error": { - "code": 280 - } - } - }, - { - "description": "change stream errors on ElectionInProgress", - "minServerVersion": "4.2", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 216, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "z": 3 - } - } - } - ], - "result": { - "error": { - "code": 216 - } - } - } - ] -} diff --git a/tests/SpecTests/change-streams/change-streams-resume-errorLabels.json b/tests/SpecTests/change-streams/change-streams-resume-errorLabels.json deleted file mode 100644 index cf8957b21..000000000 --- a/tests/SpecTests/change-streams/change-streams-resume-errorLabels.json +++ /dev/null @@ -1,1634 +0,0 @@ -{ - "collection_name": "test", - "database_name": "change-stream-tests", - "tests": [ - { - "description": "change stream resumes after HostUnreachable", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 6, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after HostNotFound", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 7, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after NetworkTimeout", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 89, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after ShutdownInProgress", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 91, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after PrimarySteppedDown", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 189, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after ExceededTimeLimit", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 262, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after SocketException", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 9001, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after NotMaster", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 10107, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after InterruptedAtShutdown", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 11600, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after InterruptedDueToReplStateChange", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 11602, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after NotMasterNoSlaveOk", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 13435, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after NotMasterOrSecondary", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 13436, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after StaleShardVersion", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 63, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after StaleEpoch", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 150, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after RetryChangeStream", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 234, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after FailedToSatisfyReadPreference", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 133, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes if error contains ResumableChangeStreamError", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 50, - "closeConnection": false, - "errorLabels": [ - "ResumableChangeStreamError" - ] - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream does not resume if error does not contain ResumableChangeStreamError", - "minServerVersion": "4.3.1", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 6, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "result": { - "error": { - "code": 6 - } - } - } - ] -} diff --git a/tests/SpecTests/change-streams/change-streams-resume-whitelist.json b/tests/SpecTests/change-streams/change-streams-resume-whitelist.json deleted file mode 100644 index 39f883ee5..000000000 --- a/tests/SpecTests/change-streams/change-streams-resume-whitelist.json +++ /dev/null @@ -1,1749 +0,0 @@ -{ - "collection_name": "test", - "database_name": "change-stream-tests", - "tests": [ - { - "description": "change stream resumes after a network error", - "minServerVersion": "4.2", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "closeConnection": true - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after HostUnreachable", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 6, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after HostNotFound", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 7, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after NetworkTimeout", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 89, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after ShutdownInProgress", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 91, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after PrimarySteppedDown", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 189, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after ExceededTimeLimit", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 262, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after SocketException", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 9001, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after NotMaster", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 10107, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after InterruptedAtShutdown", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 11600, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after InterruptedDueToReplStateChange", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 11602, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after NotMasterNoSlaveOk", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 13435, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after NotMasterOrSecondary", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 13436, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after StaleShardVersion", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 63, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after StaleEpoch", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 150, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after RetryChangeStream", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 234, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after FailedToSatisfyReadPreference", - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 133, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "change stream resumes after CursorNotFound", - "minServerVersion": "4.2", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 43, - "closeConnection": false - } - }, - "target": "collection", - "topology": [ - "replicaset", - "sharded" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": 42, - "collection": "test" - }, - "command_name": "getMore", - "database_name": "change-stream-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - } - ] -} diff --git a/tests/SpecTests/change-streams/change-streams.json b/tests/SpecTests/change-streams/change-streams.json deleted file mode 100644 index 54b76af0a..000000000 --- a/tests/SpecTests/change-streams/change-streams.json +++ /dev/null @@ -1,795 +0,0 @@ -{ - "collection_name": "test", - "database_name": "change-stream-tests", - "collection2_name": "test2", - "database2_name": "change-stream-tests-2", - "tests": [ - { - "description": "$changeStream must be the first stage in a change stream pipeline sent to the server", - "minServerVersion": "3.6.0", - "target": "collection", - "topology": [ - "replicaset" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "The server returns change stream responses in the specified server response format", - "minServerVersion": "3.6.0", - "target": "collection", - "topology": [ - "replicaset" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectations": null, - "result": { - "success": [ - { - "_id": "42", - "documentKey": "42", - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - } - ] - } - }, - { - "description": "Executing a watch helper on a Collection results in notifications for changes to the specified collection", - "minServerVersion": "3.6.0", - "target": "collection", - "topology": [ - "replicaset" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test2", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "database": "change-stream-tests-2", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "y": 2 - } - } - }, - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "z": 3 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "z": { - "$numberInt": "3" - } - } - } - ] - } - }, - { - "description": "Change Stream should allow valid aggregate pipeline stages", - "minServerVersion": "3.6.0", - "target": "collection", - "topology": [ - "replicaset" - ], - "changeStreamPipeline": [ - { - "$match": { - "fullDocument.z": 3 - } - } - ], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "y": 2 - } - } - }, - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "z": 3 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - }, - { - "$match": { - "fullDocument.z": { - "$numberInt": "3" - } - } - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "z": { - "$numberInt": "3" - } - } - } - ] - } - }, - { - "description": "Executing a watch helper on a Database results in notifications for changes to all collections in the specified database.", - "minServerVersion": "3.8.0", - "target": "database", - "topology": [ - "replicaset" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test2", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "database": "change-stream-tests-2", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "y": 2 - } - } - }, - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "z": 3 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": { - "$numberInt": "1" - }, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test2" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - }, - { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "z": { - "$numberInt": "3" - } - } - } - ] - } - }, - { - "description": "Executing a watch helper on a MongoClient results in notifications for changes to all collections in all databases in the cluster.", - "minServerVersion": "3.8.0", - "target": "client", - "topology": [ - "replicaset" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test2", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "database": "change-stream-tests-2", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "y": 2 - } - } - }, - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "z": 3 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": { - "$numberInt": "1" - }, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "command_name": "aggregate", - "database_name": "admin" - } - } - ], - "result": { - "success": [ - { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test2" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - }, - { - "operationType": "insert", - "ns": { - "db": "change-stream-tests-2", - "coll": "test" - }, - "fullDocument": { - "y": { - "$numberInt": "2" - } - } - }, - { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "z": { - "$numberInt": "3" - } - } - } - ] - } - }, - { - "description": "Test insert, update, replace, and delete event types", - "minServerVersion": "3.6.0", - "target": "collection", - "topology": [ - "replicaset" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "database": "change-stream-tests", - "collection": "test", - "name": "updateOne", - "arguments": { - "filter": { - "x": 1 - }, - "update": { - "$set": { - "x": 2 - } - } - } - }, - { - "database": "change-stream-tests", - "collection": "test", - "name": "replaceOne", - "arguments": { - "filter": { - "x": 2 - }, - "replacement": { - "x": 3 - } - } - }, - { - "database": "change-stream-tests", - "collection": "test", - "name": "deleteOne", - "arguments": { - "filter": { - "x": 3 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - }, - { - "operationType": "update", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "updateDescription": { - "updatedFields": { - "x": { - "$numberInt": "2" - } - } - } - }, - { - "operationType": "replace", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "3" - } - } - }, - { - "operationType": "delete", - "ns": { - "db": "change-stream-tests", - "coll": "test" - } - } - ] - } - }, - { - "description": "Test rename and invalidate event types", - "minServerVersion": "4.0.1", - "target": "collection", - "topology": [ - "replicaset" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "rename", - "arguments": { - "to": "test2" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "operationType": "rename", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "to": { - "db": "change-stream-tests", - "coll": "test2" - } - }, - { - "operationType": "invalidate" - } - ] - } - }, - { - "description": "Test drop and invalidate event types", - "minServerVersion": "4.0.1", - "target": "collection", - "topology": [ - "replicaset" - ], - "changeStreamPipeline": [], - "changeStreamOptions": {}, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "drop" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "operationType": "drop", - "ns": { - "db": "change-stream-tests", - "coll": "test" - } - }, - { - "operationType": "invalidate" - } - ] - } - }, - { - "description": "Test consecutive resume", - "minServerVersion": "4.1.7", - "target": "collection", - "topology": [ - "replicaset" - ], - "changeStreamPipeline": [], - "changeStreamOptions": { - "batchSize": 1 - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "getMore" - ], - "closeConnection": true - } - }, - "operations": [ - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 2 - } - } - }, - { - "database": "change-stream-tests", - "collection": "test", - "name": "insertOne", - "arguments": { - "document": { - "x": 3 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "cursor": { - "batchSize": 1 - }, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [ - { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "1" - } - } - }, - { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "2" - } - } - }, - { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "x": { - "$numberInt": "3" - } - } - } - ] - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-encrypted.json b/tests/SpecTests/client-side-encryption/corpus/corpus-encrypted.json index 998b058b0..1b72aa8a3 100644 --- a/tests/SpecTests/client-side-encryption/corpus/corpus-encrypted.json +++ b/tests/SpecTests/client-side-encryption/corpus/corpus-encrypted.json @@ -4021,5 +4021,5495 @@ "subType": "06" } } + }, + "azure_double_rand_auto_id": { + "kms": "azure", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAB0S2kOZe54q6iZqeTLndkX+kehTKtb30jTP7FS+Zx+cxhFs626OrGY+jrH41cLfroCccacyNHUZFRinfqZPNOyw==", + "subType": "06" + } + } + }, + "azure_double_rand_auto_altname": { + "kms": "azure", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAABYViH7PLjCIdmTibW9dGCJADwXx2dRSMYxEmulPu89clAoeLDa8pwJ7YxLFQCcTGmZRfmp58dDDAzV8tyyE8QMg==", + "subType": "06" + } + } + }, + "azure_double_rand_explicit_id": { + "kms": "azure", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAABeRahSj4pniBp0rLIEZE8MdeyiIKcYuTZiuGzGiXbFbntEPow88DFHIBSxbMGR7p/8jCpPL+GqBwFkPkafXbMzg==", + "subType": "06" + } + } + }, + "azure_double_rand_explicit_altname": { + "kms": "azure", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAABdaa3vKtO4cAEUjYJfOPl1KbbgeWtphfUuJd6MxR9VReNSf1jc+kONwmkPVQs2WyZ1n+TSQMGRoBp1nHRttDdTg==", + "subType": "06" + } + } + }, + "azure_double_det_explicit_id": { + "kms": "azure", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDouble": "1.2339999999999999858" + } + }, + "azure_double_det_explicit_altname": { + "kms": "azure", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDouble": "1.2339999999999999858" + } + }, + "azure_string_rand_auto_id": { + "kms": "azure", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAACeoztcDg9oZ7ixHinReWQTrAumpsfyb0E1s3BGOFHgBCi1tW79CEXfqN8riFRc1YeRTlN4k5ShgHaBWBlax+XoQ==", + "subType": "06" + } + } + }, + "azure_string_rand_auto_altname": { + "kms": "azure", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAACov9cXQvDHeKOS5Gxcxa8vdAcTsTXDYgUucGzsCyh4TnTWKGQEVk3DHndUXX569TKCjq5QsC//oWEwweCn1nZ4g==", + "subType": "06" + } + } + }, + "azure_string_rand_explicit_id": { + "kms": "azure", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAACKU5qTdMdO0buQ/37ZRANUAAafcsoNMOTxJsDOfkqUb+/kRgM1ePlwVvk4EJiAGhJ/4SEmEOpwv05TT3PxGur2Q==", + "subType": "06" + } + } + }, + "azure_string_rand_explicit_altname": { + "kms": "azure", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAACX/ODKGHUyAKxoJ/c/3lEDBTc+eP/VS8OHrLhYoP96McpnFSgYi5jfUwvrFYa715fkass4N0nAHE6TzoGTYyk6Q==", + "subType": "06" + } + } + }, + "azure_string_det_auto_id": { + "kms": "azure", + "type": "string", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAACmVI7YK4JLOzutEdQ79he817Vk5EDP/3hXwOlGmERZCtp8J8HcqClhV+pyvRLGbwmlh12fbSs9nEp7mrobQm9wA==", + "subType": "06" + } + } + }, + "azure_string_det_explicit_id": { + "kms": "azure", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAACmVI7YK4JLOzutEdQ79he817Vk5EDP/3hXwOlGmERZCtp8J8HcqClhV+pyvRLGbwmlh12fbSs9nEp7mrobQm9wA==", + "subType": "06" + } + } + }, + "azure_string_det_explicit_altname": { + "kms": "azure", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAACmVI7YK4JLOzutEdQ79he817Vk5EDP/3hXwOlGmERZCtp8J8HcqClhV+pyvRLGbwmlh12fbSs9nEp7mrobQm9wA==", + "subType": "06" + } + } + }, + "azure_object_rand_auto_id": { + "kms": "azure", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAADWkZMsfCo4dOPMH1RXC7GkZFt1RCjJf0vaLDA09ih1Jl47SOetZELQ7B1TQjRQitktzrfD43jk8Fn4J5ZYZu1qQ==", + "subType": "06" + } + } + }, + "azure_object_rand_auto_altname": { + "kms": "azure", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAADJFMymfstltZP1oAqj4bgbCk8uLGtCd12eLqvSq0ZO+JDvls7PAovwmoWwigHunP8BBXT8sLydK+jn1sHfnhrlw==", + "subType": "06" + } + } + }, + "azure_object_rand_explicit_id": { + "kms": "azure", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAADCen+XrLYKg7gIVubVfdbQwuJ0mFHxhSUUyyBWj4RCeLeLUYXckboPGixXWB9XdwcOnInfF9u6qvktY67GtYASQ==", + "subType": "06" + } + } + }, + "azure_object_rand_explicit_altname": { + "kms": "azure", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAADnUyp/7eLmxxxOdsP+mNuJABK4PQoKFWDAY7lDrH6MYa03ryASOihPZWYZWXZLrbAf7cQQhElEkKqKwY8+NXgqg==", + "subType": "06" + } + } + }, + "azure_object_det_explicit_id": { + "kms": "azure", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "azure_object_det_explicit_altname": { + "kms": "azure", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "azure_array_rand_auto_id": { + "kms": "azure", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAEtk14WyoatZcNPlg3y/XJNsBt6neFJeQwR06B9rMGV58oIsmeE5zMtUOBYTgzlnwyKpqI/XVAg8s1VxvsrvGCyLVPwGVyDztwtMgVSW6QM3s=", + "subType": "06" + } + } + }, + "azure_array_rand_auto_altname": { + "kms": "azure", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAERTO63J4Nj1BpFlqVduA2IrAiGoV4jEOH3FnFgx7ZP7da/YBmLX/bc1EqdpC8v4faHxp74iU0xAB0yW4WgySDX7rriL5cw9sMpqgLRaBxGug=", + "subType": "06" + } + } + }, + "azure_array_rand_explicit_id": { + "kms": "azure", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAEs09qQdNVwh+KFqKPREQkw0XFdRNHAvjYJzs5MDE9+QxvtKlmVKSK3wkxDdCrcH4r7ePV2nCy2h1IHYqaDnnt4s5dSawI2l88iTT+bBcCSrU=", + "subType": "06" + } + } + }, + "azure_array_rand_explicit_altname": { + "kms": "azure", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAEaQ/YL50up4YIMJuVJSiAP06IQ+YjdKLIfkN/prbOZMiXErcD1Vq1hwGhfGdpEsLVu8E7IhJb4wakVC/2dLZoRP95az6HqRRauNNZAIQMKfY=", + "subType": "06" + } + } + }, + "azure_array_det_explicit_id": { + "kms": "azure", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "azure_array_det_explicit_altname": { + "kms": "azure", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "azure_binData=00_rand_auto_id": { + "kms": "azure", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAFl/leuLAHf1p6aRKHdFyN9FM6MW2XzBemql2xQgqkwJ6YOQXW6Pu/aI1scXVOrvrSu3+wBvByjHu++1AqFgzZRQ==", + "subType": "06" + } + } + }, + "azure_binData=00_rand_auto_altname": { + "kms": "azure", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAF4Nq/LwyufT/mx0LtFSkupNHTuyjbr4yUy1N5/37XhkpqZ1e4sWCHGNaTDEm5+cvdnbqZ/MMkBv855dc8N7vnGA==", + "subType": "06" + } + } + }, + "azure_binData=00_rand_explicit_id": { + "kms": "azure", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAFv1Kbv54uXJ76Ih63vtmszQtzkXqDlv8LDCFO3sjzu70+tgRXOhLm3J8uZpwoiNkgM6oNLn0en7tnEekYB9++CA==", + "subType": "06" + } + } + }, + "azure_binData=00_rand_explicit_altname": { + "kms": "azure", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAFgcYC1n7cGGXpv0qf1Kb8t9y/6kbhscGt2QJkQpAiqadFPPYDU/wwaKdDz94NpAHMZizUbhf9tvZ3UXl1bozhDA==", + "subType": "06" + } + } + }, + "azure_binData=00_det_auto_id": { + "kms": "azure", + "type": "binData=00", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAFvswfP3+jgia6rAyrypvbso3Xm4d7MEgJRUCWFYzA+9ov++vmeirgoTp/rFavTNOPb+61fvl1WKbVwrgODusaMg==", + "subType": "06" + } + } + }, + "azure_binData=00_det_explicit_id": { + "kms": "azure", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAFvswfP3+jgia6rAyrypvbso3Xm4d7MEgJRUCWFYzA+9ov++vmeirgoTp/rFavTNOPb+61fvl1WKbVwrgODusaMg==", + "subType": "06" + } + } + }, + "azure_binData=00_det_explicit_altname": { + "kms": "azure", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAFvswfP3+jgia6rAyrypvbso3Xm4d7MEgJRUCWFYzA+9ov++vmeirgoTp/rFavTNOPb+61fvl1WKbVwrgODusaMg==", + "subType": "06" + } + } + }, + "azure_binData=04_rand_auto_id": { + "kms": "azure", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAFMzMC3BLn/zWE9dxpcD8G0h4aifSY0zSHS9xTVJXgq21s2WU++Ov2UvHatVozmtZltsUN9JvSWqOBQRkFsrXvI7bc4lYfOoOmfpTHFcRDA/c=", + "subType": "06" + } + } + }, + "azure_binData=04_rand_auto_altname": { + "kms": "azure", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAFDlBN5hUTcjamOg/sgyeG0S52kphsjUgvlpuqHYz6VVdLtZ69cGHOVqqyml3x2rVqWUZJjd4ZodOhlwWq9p+i5IYNot2QaBvi8NZSaiThTc0=", + "subType": "06" + } + } + }, + "azure_binData=04_rand_explicit_id": { + "kms": "azure", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAFjvS2ozJuAL3rCvyBpraVtgL91OMdiskmgYnyfKlzd8EhYLd1cL4yxnTUjRXx+W+p8uN0/QZo+mynhcWnwcq83raY+I1HftSTx+S6rZ0qyDM=", + "subType": "06" + } + } + }, + "azure_binData=04_rand_explicit_altname": { + "kms": "azure", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAFqUMd/I0yOdy5W4THvFc6yrgSzB6arkRs/06b0M9Ii+QtAY6vbz+/aJ0Iy3Jm8TahC1wOZVmTj5luQpr+PHZMCEAFadv+0K/Nsx6xVhAh9gg=", + "subType": "06" + } + } + }, + "azure_binData=04_det_auto_id": { + "kms": "azure", + "type": "binData=04", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAFmN+KMrERGmfmue8/hG4D+ZcGzxC2HntdYBLjEolzvS9FV5JH/adxyUAnMpyL8FNznARL51rbv/G1nXPn9mPabsQ4BtWEAQbHx9TiXd+xbB0=", + "subType": "06" + } + } + }, + "azure_binData=04_det_explicit_id": { + "kms": "azure", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAFmN+KMrERGmfmue8/hG4D+ZcGzxC2HntdYBLjEolzvS9FV5JH/adxyUAnMpyL8FNznARL51rbv/G1nXPn9mPabsQ4BtWEAQbHx9TiXd+xbB0=", + "subType": "06" + } + } + }, + "azure_binData=04_det_explicit_altname": { + "kms": "azure", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAFmN+KMrERGmfmue8/hG4D+ZcGzxC2HntdYBLjEolzvS9FV5JH/adxyUAnMpyL8FNznARL51rbv/G1nXPn9mPabsQ4BtWEAQbHx9TiXd+xbB0=", + "subType": "06" + } + } + }, + "azure_undefined_rand_explicit_id": { + "kms": "azure", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "azure_undefined_rand_explicit_altname": { + "kms": "azure", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "azure_undefined_det_explicit_id": { + "kms": "azure", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "azure_undefined_det_explicit_altname": { + "kms": "azure", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "azure_objectId_rand_auto_id": { + "kms": "azure", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAH3sYVJpCKi310YxndMwm5ltEbbiRO1RwZxxeEkzI8tptbNXC8t7RkrT8VSJZ43wbGYCiqH5RZy9v8pYwtUm4STw==", + "subType": "06" + } + } + }, + "azure_objectId_rand_auto_altname": { + "kms": "azure", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAHD7agzVEc0JwesHHhkpGYIDAHQ+3Hc691kqic6YmVvK2N45fD5aRKftaZNs5OxSj3tNHSo7lQ+DVtPj8uSSpsVg==", + "subType": "06" + } + } + }, + "azure_objectId_rand_explicit_id": { + "kms": "azure", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAHEgKgy2mpMLpfeEWqbvQOaRZAy+cEGXGon3e53/JoH6dZneEyyt4ZrcrK6uRqyUPWX0q104JbCYxfbtHtdzWgPQ==", + "subType": "06" + } + } + }, + "azure_objectId_rand_explicit_altname": { + "kms": "azure", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAHqSv6Nruw3TIi7y0FPRjSfnJmWSdv5XMhAtnHNkT8MVuHeM32ayo0yc8dTA1wlkRtAI5JrGxTfERCXYuCojvvXg==", + "subType": "06" + } + } + }, + "azure_objectId_det_auto_id": { + "kms": "azure", + "type": "objectId", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAHcPRjIOyLDUJCDcdWkUySKCFS2AFkIa1OQyQAfC3Zh5HwJ1O7j2o+iYKRerhbni8lBiZH7EUMm1JcxM99lLC5jQ==", + "subType": "06" + } + } + }, + "azure_objectId_det_explicit_id": { + "kms": "azure", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAHcPRjIOyLDUJCDcdWkUySKCFS2AFkIa1OQyQAfC3Zh5HwJ1O7j2o+iYKRerhbni8lBiZH7EUMm1JcxM99lLC5jQ==", + "subType": "06" + } + } + }, + "azure_objectId_det_explicit_altname": { + "kms": "azure", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAHcPRjIOyLDUJCDcdWkUySKCFS2AFkIa1OQyQAfC3Zh5HwJ1O7j2o+iYKRerhbni8lBiZH7EUMm1JcxM99lLC5jQ==", + "subType": "06" + } + } + }, + "azure_bool_rand_auto_id": { + "kms": "azure", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAIYVWPvzSmiCs9LwRlv/AoQWhaS5mzoKX4W26M5eg/gPjOZbEVYOV80pWMxCcZWRAyV/NDWDUmKtRQDMU9b8lCJw==", + "subType": "06" + } + } + }, + "azure_bool_rand_auto_altname": { + "kms": "azure", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAIsAB01Ugqtw4T9SkuJBQN1y/ewpRAyz0vjFPdKI+jmPMmaXpMlXDJU8ZbTKm/nh6sjJCFcY5oZJ83ylbp2gHc6w==", + "subType": "06" + } + } + }, + "azure_bool_rand_explicit_id": { + "kms": "azure", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAIr8/qFd564X1mqHEhB0y7bzGFdrHuw+Gk45nXla3VvGHzeIJy6j2Wdl0uziWslMmBvNp8WweW+jQ6E2Fu7SiojQ==", + "subType": "06" + } + } + }, + "azure_bool_rand_explicit_altname": { + "kms": "azure", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAIWsca5FAnS2zhHnmKmexvvXMTgsZZ7uAFHnjQassUcay6mvIWH4hOnGiRxt5Zm0wO4S6cZq+PZrmEH5/n9rJcJQ==", + "subType": "06" + } + } + }, + "azure_bool_det_explicit_id": { + "kms": "azure", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": true + }, + "azure_bool_det_explicit_altname": { + "kms": "azure", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": true + }, + "azure_date_rand_auto_id": { + "kms": "azure", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAJwKo7XW5daIFlwY1mDAnJdHlcUgF+74oViL28hQGhde63pkPyyS6lPkYrc1gcCK5DL7PwsSX4Vb9SsNAG9860xw==", + "subType": "06" + } + } + }, + "azure_date_rand_auto_altname": { + "kms": "azure", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAJYZdWIqvqTztGKJkSASMEOjyrUFKnYql8fMIEzfEZWx2BYsIkxxOUUUCASg/Jsn09fTLVQ7yLD+LwycuI2uaXsw==", + "subType": "06" + } + } + }, + "azure_date_rand_explicit_id": { + "kms": "azure", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAJuWzKqi3KV8GbGGnT7i9N4BACUuNjt5AgKsjWIfrWRXK1+jRQFq0bYlVWaliT9CNIygL2aTF0H4eHl55PAI84MQ==", + "subType": "06" + } + } + }, + "azure_date_rand_explicit_altname": { + "kms": "azure", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAJ5JTtTuP4zTnEbaVlS/W59SrZ08LOC4ZIl+h+H4RnfHUfBXDwUou+APolVaYko+VZMKecrikdPeewgzWaqazJ1g==", + "subType": "06" + } + } + }, + "azure_date_det_auto_id": { + "kms": "azure", + "type": "date", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAJCREIp/SPolAZcVU1iOmaJaN2tFId5HhrjNmhp6xhA1AIPLnN+U7TAqesxFN7iebR9fXI5fZxYNgyWqQC1rqUJw==", + "subType": "06" + } + } + }, + "azure_date_det_explicit_id": { + "kms": "azure", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAJCREIp/SPolAZcVU1iOmaJaN2tFId5HhrjNmhp6xhA1AIPLnN+U7TAqesxFN7iebR9fXI5fZxYNgyWqQC1rqUJw==", + "subType": "06" + } + } + }, + "azure_date_det_explicit_altname": { + "kms": "azure", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAJCREIp/SPolAZcVU1iOmaJaN2tFId5HhrjNmhp6xhA1AIPLnN+U7TAqesxFN7iebR9fXI5fZxYNgyWqQC1rqUJw==", + "subType": "06" + } + } + }, + "azure_null_rand_explicit_id": { + "kms": "azure", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "azure_null_rand_explicit_altname": { + "kms": "azure", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "azure_null_det_explicit_id": { + "kms": "azure", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "azure_null_det_explicit_altname": { + "kms": "azure", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "azure_regex_rand_auto_id": { + "kms": "azure", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAALsMm3W2ogEiI6m0l8dS5Xhqnw+vMBvN1EesOTqAZOk4tQleX6fWARwUUnjFxbuejU7ISb50fc/Ul+ntL9z/2nHQ==", + "subType": "06" + } + } + }, + "azure_regex_rand_auto_altname": { + "kms": "azure", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAALITQNQI0hfCeMTxH0Hce1Cf5tinQG+Bq8EolUACvxUUQcDqIXfFXn19tV/Qyj4lIdnnwh/18hiswgEpJRK7uLGw==", + "subType": "06" + } + } + }, + "azure_regex_rand_explicit_id": { + "kms": "azure", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAALw/1QI/bKeiGUrrtC+yXOTvxZ2mJjSelPPGOm1mge0ws8DsX0DPHmo6MjhnRO4u0c/LWiE3hwHG2rYjAFlFXZ5A==", + "subType": "06" + } + } + }, + "azure_regex_rand_explicit_altname": { + "kms": "azure", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAL6Sl58UfFCHCZzWIB4r19/ZjeSRAoWeTFCFedKiwyR8/xnL+8jzXK/9+vTIspP6j35lFapr+f4iBNB9WjdpYNKA==", + "subType": "06" + } + } + }, + "azure_regex_det_auto_id": { + "kms": "azure", + "type": "regex", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAALxshM91Tsql/8kPe3dC16oP36XSUIN6godiRVIJLJ+NAwYtEkThthQsln7CrkIxIx6npN6A/hw1CBJERS/cqWhw==", + "subType": "06" + } + } + }, + "azure_regex_det_explicit_id": { + "kms": "azure", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAALxshM91Tsql/8kPe3dC16oP36XSUIN6godiRVIJLJ+NAwYtEkThthQsln7CrkIxIx6npN6A/hw1CBJERS/cqWhw==", + "subType": "06" + } + } + }, + "azure_regex_det_explicit_altname": { + "kms": "azure", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAALxshM91Tsql/8kPe3dC16oP36XSUIN6godiRVIJLJ+NAwYtEkThthQsln7CrkIxIx6npN6A/hw1CBJERS/cqWhw==", + "subType": "06" + } + } + }, + "azure_dbPointer_rand_auto_id": { + "kms": "azure", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAMaAd1v/XCYM2Kzi/f4utR6aHOFORmzZ17EepEjkn5IeKshktUpPWjI/dBwSunn5Qxx2zI3nm06c3SDvp6tw8qb7u4qXjLQYhlsQ0bHvvm+vE=", + "subType": "06" + } + } + }, + "azure_dbPointer_rand_auto_altname": { + "kms": "azure", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAM6VNjkN9bMIzfC7AX0ZhOEXPpyPE0nzYq3c5TNHrgeGWdZDR9GVdbO9t55zQrQJJ2Mmevh8c0WaAUV+YODv7ty6TDBsPbaKWWqMzu/v9RXHo=", + "subType": "06" + } + } + }, + "azure_dbPointer_rand_explicit_id": { + "kms": "azure", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAM66tywuMhwdyUjxfl7EOdKHNCLeIPnct3PgKrAKlOQFjiNQUIA2ShVy0qYpJcvvFsuQ5e8Bjr0IqeBc8mC7n4euRSM1UXpLqI5XHgXMMaYpI=", + "subType": "06" + } + } + }, + "azure_dbPointer_rand_explicit_altname": { + "kms": "azure", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAMtPQEbZ4gWoSYjVZLd5X6j0XxutWY1Ecrys2ErKRgZaxP0uGe8uw0cnr2Z5PYylaYmsSicLwD1PwWY42PKmaGBDraHmdfqDOPvrNxhBrfU/E=", + "subType": "06" + } + } + }, + "azure_dbPointer_det_auto_id": { + "kms": "azure", + "type": "dbPointer", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAMxUcVqq6RpAUCv08qGkmjuwVAIgLeYyh7xZnMeCYVGmhJKIP1Zdt1SvRGRV0jzwCQmXgxNd04adRwJnG/PRQIsL9aH3ilJgEnUbOo1nqR7yw=", + "subType": "06" + } + } + }, + "azure_dbPointer_det_explicit_id": { + "kms": "azure", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAMxUcVqq6RpAUCv08qGkmjuwVAIgLeYyh7xZnMeCYVGmhJKIP1Zdt1SvRGRV0jzwCQmXgxNd04adRwJnG/PRQIsL9aH3ilJgEnUbOo1nqR7yw=", + "subType": "06" + } + } + }, + "azure_dbPointer_det_explicit_altname": { + "kms": "azure", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAMxUcVqq6RpAUCv08qGkmjuwVAIgLeYyh7xZnMeCYVGmhJKIP1Zdt1SvRGRV0jzwCQmXgxNd04adRwJnG/PRQIsL9aH3ilJgEnUbOo1nqR7yw=", + "subType": "06" + } + } + }, + "azure_javascript_rand_auto_id": { + "kms": "azure", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAANWXPb5z3a0S7F26vkmBF3fV+oXYUj15OEtnSlXlUrc+gbhbPDxSvCPnTBEy5sNu4ndkvEZZxYgZInkF2q4rhlfQ==", + "subType": "06" + } + } + }, + "azure_javascript_rand_auto_altname": { + "kms": "azure", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAANN4mcwLz/J4eOUknhVsy6kdF1ThDP8cx6dNpOwJWAiyPHEsn+i6JmMTlfQMBrUp9HB/u3R+jLO5yz4XgLUKE8Tw==", + "subType": "06" + } + } + }, + "azure_javascript_rand_explicit_id": { + "kms": "azure", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAANJ+t5Z8hSQaoNzszzkWndAo4A0avDf9bKFa7euznz8ZYInnl9RUVqWMyxjSuIotAvTyYSJzxh+w2hKCgVf+MjEA==", + "subType": "06" + } + } + }, + "azure_javascript_rand_explicit_altname": { + "kms": "azure", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAANRLOQFpmkEg/KdWMmaurkNtUhy45rgtoipc9kQz6olgDWiMim81XC0AW5cOvjbHXL3w7Du28Kwdsp4j0PTTXHUQ==", + "subType": "06" + } + } + }, + "azure_javascript_det_auto_id": { + "kms": "azure", + "type": "javascript", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAANUrNUS/7/dmKVWBd+2JKGEn1hxbFSyu3p5sDNatukG2m16t4WwxzmYAg8PuQbAxekprs7iaLA+7D2Kn3ZuMSQOw==", + "subType": "06" + } + } + }, + "azure_javascript_det_explicit_id": { + "kms": "azure", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAANUrNUS/7/dmKVWBd+2JKGEn1hxbFSyu3p5sDNatukG2m16t4WwxzmYAg8PuQbAxekprs7iaLA+7D2Kn3ZuMSQOw==", + "subType": "06" + } + } + }, + "azure_javascript_det_explicit_altname": { + "kms": "azure", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAANUrNUS/7/dmKVWBd+2JKGEn1hxbFSyu3p5sDNatukG2m16t4WwxzmYAg8PuQbAxekprs7iaLA+7D2Kn3ZuMSQOw==", + "subType": "06" + } + } + }, + "azure_symbol_rand_auto_id": { + "kms": "azure", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAORMcgtQSU+/2Qlq57neRrVuAFSeSwkqdo+z1fh6IKjyEzhCy+u5bTzSzTopyKJQTCUZA2mSpRezWkM87oiGfhMFkBRVreMcE62eH+BLlgUaM=", + "subType": "06" + } + } + }, + "azure_symbol_rand_auto_altname": { + "kms": "azure", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAOIKlAw/A3nwHn0tO2cYtJx0azB8MGmXtt+bRptzn8yHlUSpMpYaiU0ssBBiLkmMLAITYebLqDk3NHESyP7PvbSfX1E2XVn2Nf694ZqPWMec8=", + "subType": "06" + } + } + }, + "azure_symbol_rand_explicit_id": { + "kms": "azure", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAO8SXW76AEr/6D6zyP1RYwmwdVM2AINaXZn3Ipy+fynWTUV6XIPIRR7xMTttNo2zlh7fgXDZ28PmjooGlQzn0q0JVQmXPCIPM3aqAmMcgyuqg=", + "subType": "06" + } + } + }, + "azure_symbol_rand_explicit_altname": { + "kms": "azure", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAOtoJWm2Ucre0foHIiOutsX1WIyub7t3Lby3/F8zRXn+l6ixlTjAPgWFwpRnYg96Lt2ACDDQ9CO51ejr9qk0b8LDBwG3qU5Cuibsp7vo1VsdI=", + "subType": "06" + } + } + }, + "azure_symbol_det_auto_id": { + "kms": "azure", + "type": "symbol", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAOvp/FMMmWVMkiuN51uFMFBiRQAcc9jftlNsHsLoNtohZaGni26kgX94b+/EI8pdWF5xA/73JlGlij0Rt+vC9s/zTDItRpn0bJL54WPphDcmA=", + "subType": "06" + } + } + }, + "azure_symbol_det_explicit_id": { + "kms": "azure", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAOvp/FMMmWVMkiuN51uFMFBiRQAcc9jftlNsHsLoNtohZaGni26kgX94b+/EI8pdWF5xA/73JlGlij0Rt+vC9s/zTDItRpn0bJL54WPphDcmA=", + "subType": "06" + } + } + }, + "azure_symbol_det_explicit_altname": { + "kms": "azure", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAOvp/FMMmWVMkiuN51uFMFBiRQAcc9jftlNsHsLoNtohZaGni26kgX94b+/EI8pdWF5xA/73JlGlij0Rt+vC9s/zTDItRpn0bJL54WPphDcmA=", + "subType": "06" + } + } + }, + "azure_javascriptWithScope_rand_auto_id": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAPCw9NnvJyuTYIgZxr1w1UiG85PGZ4rO62DWWDF98HwVM/Y6u7hNdNjkaWjYFsPMl38ioHw/pS8GFR62QmH2RAw/BV0wI7pNy2evANr3i3gKg=", + "subType": "06" + } + } + }, + "azure_javascriptWithScope_rand_auto_altname": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAPXQzqnQ2UWkIYof8/OfadNMa7iVKAbOaiu7YGm8iVrx+W6uxKLPFugVqHtQ29hYXXf33xr8rqGNxDlAe7/x1OeYEif71f7LUkmKF9WxJV9Ko=", + "subType": "06" + } + } + }, + "azure_javascriptWithScope_rand_explicit_id": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAP0nxlppgPyjLx0eBempbOlL21G6KbABSrE6+YuNDcsjJjxCQuLR9+aoAwa+yCDEC7GZ1E3oP489edKUuNpE4Ts26jy4aRegu4DmyECUeBwAg=", + "subType": "06" + } + } + }, + "azure_javascriptWithScope_rand_explicit_altname": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAPO89afu9Sb+cK9wwM1cO1DPjvu5UNyObjjTScy1hy9PzllJGfj7b84f0Ah74jPYsMPwI0Eslu/IYF3+5jmquq5Qp/VUQESlxqRqRK0xIeMfs=", + "subType": "06" + } + } + }, + "azure_javascriptWithScope_det_explicit_id": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "azure_javascriptWithScope_det_explicit_altname": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "azure_int_rand_auto_id": { + "kms": "azure", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAQUyy4uWmWdzypsK81q9egREg4s80X3L2hzxJzC+fL08Xzy1z9grpPPCfJrluUVKMMGmmZR8gJPJ70igN3unJbzg==", + "subType": "06" + } + } + }, + "azure_int_rand_auto_altname": { + "kms": "azure", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAQr4gyoHKpGsSJo8CMsYSJk/KilFMJhsDCmxrha7yfNW1uR5sjyZj4B4s6uTXGw76x7aR/AvecDlY3QFJb8L1mjg==", + "subType": "06" + } + } + }, + "azure_int_rand_explicit_id": { + "kms": "azure", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAQ0zgXYPV1MuEFksmDpVDoWkoZQelm3+rYrMiT64KYywO//75799W8TbR3a7O6Q/ErjKQOin2OCp8EWwZqTDdz5w==", + "subType": "06" + } + } + }, + "azure_int_rand_explicit_altname": { + "kms": "azure", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAQG+qz00yizREbP3tla1elMiwf8TKLbUU2XWUP+E0vey/wvbjTTIzqwUlz/b9St77CHJhavypP3hMrngXR9GapbQ==", + "subType": "06" + } + } + }, + "azure_int_det_auto_id": { + "kms": "azure", + "type": "int", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAQCkJH+CataLqp/xBjO77QBprC2xPV+rE+goSZ3C6aqwXIeTYHTOqEbeaFb5iZcqYH5nWvNvnfbZSIMyvSfrPjhw==", + "subType": "06" + } + } + }, + "azure_int_det_explicit_id": { + "kms": "azure", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAQCkJH+CataLqp/xBjO77QBprC2xPV+rE+goSZ3C6aqwXIeTYHTOqEbeaFb5iZcqYH5nWvNvnfbZSIMyvSfrPjhw==", + "subType": "06" + } + } + }, + "azure_int_det_explicit_altname": { + "kms": "azure", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAQCkJH+CataLqp/xBjO77QBprC2xPV+rE+goSZ3C6aqwXIeTYHTOqEbeaFb5iZcqYH5nWvNvnfbZSIMyvSfrPjhw==", + "subType": "06" + } + } + }, + "azure_timestamp_rand_auto_id": { + "kms": "azure", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAARwcXYtx+A7g/zGkjGdkyVxZGCO9Nzj3D70NIpl2TeH2j9qYGP4DenwL1xSgrL2Ez+X58d2BvNhKrjA9y2w1Z8kA==", + "subType": "06" + } + } + }, + "azure_timestamp_rand_auto_altname": { + "kms": "azure", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAARQ0Pjx3l92Aqhn2e1hot2M9rQ6aLPE2Iw8AVhm5AD8FWywWih12Fn2p9+kiE33yKPOCyrTWQHKPtB4yYhqnJgGg==", + "subType": "06" + } + } + }, + "azure_timestamp_rand_explicit_id": { + "kms": "azure", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAARvFMlIzh2IjpHkTJ8buqTOqBA0+CxVDsZacUhSHVMgJLN+0DJsJy8OfkmKMu9Lk5hULY00Udoja87x+79mYfmeQ==", + "subType": "06" + } + } + }, + "azure_timestamp_rand_explicit_altname": { + "kms": "azure", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAAR+2SCd7V5ukAkh7CYpNPIatzTL8osNoA4Mb5jjjbos8eMamImw0fbH8YA+Rdm4CgGdQQ9VDX7MtMWlArkj0Jpew==", + "subType": "06" + } + } + }, + "azure_timestamp_det_auto_id": { + "kms": "azure", + "type": "timestamp", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAARe72T/oC09QGE1vuljb6ZEHa6llEwMLT+C4s9u1fREkOKndpmrOlGE8zOey4teizY1ypOMkIZ8GDQJJ4kLSpNkQ==", + "subType": "06" + } + } + }, + "azure_timestamp_det_explicit_id": { + "kms": "azure", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAARe72T/oC09QGE1vuljb6ZEHa6llEwMLT+C4s9u1fREkOKndpmrOlGE8zOey4teizY1ypOMkIZ8GDQJJ4kLSpNkQ==", + "subType": "06" + } + } + }, + "azure_timestamp_det_explicit_altname": { + "kms": "azure", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAARe72T/oC09QGE1vuljb6ZEHa6llEwMLT+C4s9u1fREkOKndpmrOlGE8zOey4teizY1ypOMkIZ8GDQJJ4kLSpNkQ==", + "subType": "06" + } + } + }, + "azure_long_rand_auto_id": { + "kms": "azure", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAASSSgX7k8iw0xFe0AiIzOu0e0P7Ujyfsk/Cdl0fR5X8V3QLVER+1Qa47Qpb8iWL2VLBSh+55HvIEtvhWn8SwXaog==", + "subType": "06" + } + } + }, + "azure_long_rand_auto_altname": { + "kms": "azure", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAASUhKr5K7ulGTeFbhIvJ2DDE10gRAFn5+2zqnsIFSY8lYV2PBYcENdeNBXZs6kyIAYhJdQyuOChVCerTI5jmQWDw==", + "subType": "06" + } + } + }, + "azure_long_rand_explicit_id": { + "kms": "azure", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAASHxawpjTHdXYRWQSZ7Qi7gFC+o4dW2mPH8s5nQkPFY/EubcJbdAZ5HFp66NfPaDJ/NSH6Vy+TkpX3683RC+bjSQ==", + "subType": "06" + } + } + }, + "azure_long_rand_explicit_altname": { + "kms": "azure", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAASVaMAv6UjuBOUZMJ9qz+58TQWmgaMpS9xrJziJY80ml9aRlDTtRubP7U40CgbDvrtY1QgHbkF/di1XDCB6iXMMg==", + "subType": "06" + } + } + }, + "azure_long_det_auto_id": { + "kms": "azure", + "type": "long", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAS06L8oEPeMvVlA32VlobdOWG24OoyMbv9PyYsHLsbT0bHFwU7lYUSQG9EkYVRNPEDzvXpciE1jT7KT8CRY8XT/g==", + "subType": "06" + } + } + }, + "azure_long_det_explicit_id": { + "kms": "azure", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAS06L8oEPeMvVlA32VlobdOWG24OoyMbv9PyYsHLsbT0bHFwU7lYUSQG9EkYVRNPEDzvXpciE1jT7KT8CRY8XT/g==", + "subType": "06" + } + } + }, + "azure_long_det_explicit_altname": { + "kms": "azure", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQGVERAAAAAAAAAAAAAAAAAS06L8oEPeMvVlA32VlobdOWG24OoyMbv9PyYsHLsbT0bHFwU7lYUSQG9EkYVRNPEDzvXpciE1jT7KT8CRY8XT/g==", + "subType": "06" + } + } + }, + "azure_decimal_rand_auto_id": { + "kms": "azure", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAATJ6LZgPu9F+rPtYsMuvwOx62+g1dAk858BUtE9FjC/300DnbDiolhkHNcyoFs07NYUNgLthW2rISb/ejmsDCt/oqnf8zWYf9vrJEfHaS/Ocw=", + "subType": "06" + } + } + }, + "azure_decimal_rand_auto_altname": { + "kms": "azure", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAATX8eD6qFYWKwIGvXtQG79fXKuPW9hkIV0OwrmNNIqRltw6gPHl+/1X8Q6rgmjCxqvhB05AxTj7xz64gP+ILkPQY8e8VGuCOvOdwDo2IPwy18=", + "subType": "06" + } + } + }, + "azure_decimal_rand_explicit_id": { + "kms": "azure", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAATBjQ9E5wDdTS/iI1XDqGmDBC5aLbPB4nSyrjRLfv1zEoPRjmcHlQmMRJA0mori2VQv6EBFNHeczFCenJaSAkuh77czeXM2vH3T6qwEIDs4dw=", + "subType": "06" + } + } + }, + "azure_decimal_rand_explicit_altname": { + "kms": "azure", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgGVERAAAAAAAAAAAAAAAAATtkjbhdve7MNuLaTm6qvaewuVUxeC1DMz1fd4RC4jeiBFMd5uZUVJTiOIerwQ6P5G5lkMlezKDWgKl2FUvZH6c7V3JknhsaWcV5iLWGUL6Zc=", + "subType": "06" + } + } + }, + "azure_decimal_det_explicit_id": { + "kms": "azure", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "azure_decimal_det_explicit_altname": { + "kms": "azure", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "azure_minKey_rand_explicit_id": { + "kms": "azure", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "azure_minKey_rand_explicit_altname": { + "kms": "azure", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "azure_minKey_det_explicit_id": { + "kms": "azure", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "azure_minKey_det_explicit_altname": { + "kms": "azure", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "azure_maxKey_rand_explicit_id": { + "kms": "azure", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "azure_maxKey_rand_explicit_altname": { + "kms": "azure", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "azure_maxKey_det_explicit_id": { + "kms": "azure", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "azure_maxKey_det_explicit_altname": { + "kms": "azure", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "gcp_double_rand_auto_id": { + "kms": "gcp", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAABFoHQxnh1XSC0k1B01uFFg7rE9sZVBn4PXo26JX8gx9tuxu+4l9Avb23H9BfOzuWiEc43iw87K/W2y0VfKp5CCg==", + "subType": "06" + } + } + }, + "gcp_double_rand_auto_altname": { + "kms": "gcp", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAABRkZkEtQEFB/r268cNfYRQbN4u5Cxjl9Uh+8wq9TFWLQH2E/9wj2vTLlxQ2cQsM7Qd+XxR5idjfBf9CKAfvUa/A==", + "subType": "06" + } + } + }, + "gcp_double_rand_explicit_id": { + "kms": "gcp", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAABDSUZ+0BbDDEZxCXA+J2T6Js8Uor2dfXSf7s/hpLrg6dxcW2chpht9XLiLOXG5w83TzCAI5pF8cQgBpBpYjR8RQ==", + "subType": "06" + } + } + }, + "gcp_double_rand_explicit_altname": { + "kms": "gcp", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAABCYxugs7L+4S+1rr0VILSbtBm79JPTLuzluQAv0+8hbu5Z6zReOL6Ta1vQH1oA+pSPGYA4euye3zNl1X6ZewbPw==", + "subType": "06" + } + } + }, + "gcp_double_det_explicit_id": { + "kms": "gcp", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDouble": "1.2339999999999999858" + } + }, + "gcp_double_det_explicit_altname": { + "kms": "gcp", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDouble": "1.2339999999999999858" + } + }, + "gcp_string_rand_auto_id": { + "kms": "gcp", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAACx3wSslJEiD80YLTH0n4Bbs4yWVPQl15AU8pZMLLQePqEtI+BJy3t2bqNP1098jS0CGSf+LQmQvXhJn1aNFeMTw==", + "subType": "06" + } + } + }, + "gcp_string_rand_auto_altname": { + "kms": "gcp", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAC5BTe5KP5UxSIk6dJlkz8aaZ/9fg44XPWHafiiL/48lcv3AWbu2gcBo1EDuc1sJQu6XMrtDCRQ7PCHsL7sEQMGQ==", + "subType": "06" + } + } + }, + "gcp_string_rand_explicit_id": { + "kms": "gcp", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAACyJN55OcyXXJ71x8VphTaIuIg6kQtGgVKPhWx0LSdYc6JOjB6LTdA7SEWiSlSWWFZE26UmKcPbkbLDAYf4IVrzQ==", + "subType": "06" + } + } + }, + "gcp_string_rand_explicit_altname": { + "kms": "gcp", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAACoa0d9gqfPP5s3+GoruwzxoQFgli8SmjpTVRLAOcFxqGdfrwSbpYffSw/OR45sZPxXCL6T2MtUvZsl7ukv0jBnw==", + "subType": "06" + } + } + }, + "gcp_string_det_auto_id": { + "kms": "gcp", + "type": "string", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAACTCkyETcWayIZ9YEoQEBVIF3i7iXEe6M3KjYYaSVCYdqSbSHBzlwKWYbP+Xj/MMYBYTLZ1aiRQWCMK4gWPYppZw==", + "subType": "06" + } + } + }, + "gcp_string_det_explicit_id": { + "kms": "gcp", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAACTCkyETcWayIZ9YEoQEBVIF3i7iXEe6M3KjYYaSVCYdqSbSHBzlwKWYbP+Xj/MMYBYTLZ1aiRQWCMK4gWPYppZw==", + "subType": "06" + } + } + }, + "gcp_string_det_explicit_altname": { + "kms": "gcp", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAACTCkyETcWayIZ9YEoQEBVIF3i7iXEe6M3KjYYaSVCYdqSbSHBzlwKWYbP+Xj/MMYBYTLZ1aiRQWCMK4gWPYppZw==", + "subType": "06" + } + } + }, + "gcp_object_rand_auto_id": { + "kms": "gcp", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAADy+8fkyeNYdIK001YogXfKc25zRXS1VGIFVWR6jRfrexy9C8LBBfX3iDwGNPbP2pkC3Tq16OoziQB6iNGf7s7yg==", + "subType": "06" + } + } + }, + "gcp_object_rand_auto_altname": { + "kms": "gcp", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAADixoDdvm57gH8ooOaKI57WyZD5uaPmuYgmrgAFuV8I+oaalqYctnNSYlzQKCMQX/mIcTxvW3oOWY7+IzAz7npvw==", + "subType": "06" + } + } + }, + "gcp_object_rand_explicit_id": { + "kms": "gcp", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAADvq0OAoijgHaVMhsoNMdfWFLyISDo6Y13sYM0CoBXS/oXJNIJJvhgKPbFSV/h4IgiDLy4qNYOTJQvpqt094RPgQ==", + "subType": "06" + } + } + }, + "gcp_object_rand_explicit_altname": { + "kms": "gcp", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAADuTZF7/uqGjFbjzBYspPkxGWvvVAEN/ib8bfPOQrEobtTWuU+ju9H3TlT9DMuFy7RdUZnPB0D3HkM8+zky5xeBw==", + "subType": "06" + } + } + }, + "gcp_object_det_explicit_id": { + "kms": "gcp", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "gcp_object_det_explicit_altname": { + "kms": "gcp", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "gcp_array_rand_auto_id": { + "kms": "gcp", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAE085kJIBX6S93D94bcRjkOegEKsksi2R1cxoVDoOpSdHh3S6bZAOh50W405wvnOKf3KTP9SICDUehQKQZSC026Y5dwVQ2GiM7PtpSedthKJs=", + "subType": "06" + } + } + }, + "gcp_array_rand_auto_altname": { + "kms": "gcp", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAEk/FAXsaqyVr6I+MY5L0axeLhskcEfLZeB8whLMKbjLDLa8Iep+IdrFVSfKo03Zr/7Ah8Js01aT6+Vt4EDMJK0mGKZJOjsrAf3b6RS+Mzebg=", + "subType": "06" + } + } + }, + "gcp_array_rand_explicit_id": { + "kms": "gcp", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAEDY7J9JGiurctYr7ytakNjcryVm42fkubcVpQpUYEkpK/G9NLGjrJuFgNW5ZVjYiPKEBbDB7vEtJqGux0BU++hrvVHNJ3wUT2mbDE18NE4KE=", + "subType": "06" + } + } + }, + "gcp_array_rand_explicit_altname": { + "kms": "gcp", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAErFFlw8W9J2y+751RnYLw0TSK9ThD6sP3i4zPbZtiuhc90RFoJhScvqM9i4sDKuYePZZRLBxdX4EZhZClOmswCGDLCIWsQlSvCwgDcIsRR/w=", + "subType": "06" + } + } + }, + "gcp_array_det_explicit_id": { + "kms": "gcp", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "gcp_array_det_explicit_altname": { + "kms": "gcp", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "gcp_binData=00_rand_auto_id": { + "kms": "gcp", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAF0R5BNkQKfm6wx/tob8nVGDEYV/pvy9UeCqc9gFNuB5d9KxCkgyxryV65rbB90OriqvWFO2jcxzchRYgRI3fQ+A==", + "subType": "06" + } + } + }, + "gcp_binData=00_rand_auto_altname": { + "kms": "gcp", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAF4wcT8XGc3xNdKYDX5/cbUwPDdnkIXlWWCCYeSXSk2oWPxMZnPsVQ44nXKJJsKitoE3r/hL1sSG5239WzCWyx9g==", + "subType": "06" + } + } + }, + "gcp_binData=00_rand_explicit_id": { + "kms": "gcp", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAF07OFs5mlx0AB6QBanaybLuhuFbG+19KxSqHlSgELcz6TQKI6equX97OZdaWSWf2SSeiYm5E6+Y3lgA5l4KxC2A==", + "subType": "06" + } + } + }, + "gcp_binData=00_rand_explicit_altname": { + "kms": "gcp", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAFZ74Q7JMm7y2i3wRmjIRKefhmdnrhP1NXJgploi+44eQ2eRraZsW7peGPYyIfsXEbhgV5+aLmiYgvemBywfdogQ==", + "subType": "06" + } + } + }, + "gcp_binData=00_det_auto_id": { + "kms": "gcp", + "type": "binData=00", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAFhwJkocj36WXoY3mg2GWUrJ5IQTo9MvkwEwRFKdkcxm9pX2PZPK7bN5ZWw3IFcQ/0GfaW6V4LYr8WarZdLF0p5g==", + "subType": "06" + } + } + }, + "gcp_binData=00_det_explicit_id": { + "kms": "gcp", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAFhwJkocj36WXoY3mg2GWUrJ5IQTo9MvkwEwRFKdkcxm9pX2PZPK7bN5ZWw3IFcQ/0GfaW6V4LYr8WarZdLF0p5g==", + "subType": "06" + } + } + }, + "gcp_binData=00_det_explicit_altname": { + "kms": "gcp", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAFhwJkocj36WXoY3mg2GWUrJ5IQTo9MvkwEwRFKdkcxm9pX2PZPK7bN5ZWw3IFcQ/0GfaW6V4LYr8WarZdLF0p5g==", + "subType": "06" + } + } + }, + "gcp_binData=04_rand_auto_id": { + "kms": "gcp", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAFmDO47RTVXzm8D4hfhLICILrQJg3yOwG3HYfCdz7yaanPow2Y6bMxvXxk+kDS29aS8pJKDqJQQoMGc1ZFD3yYKsLQHRi/8rW6TNDQd4sCQ00=", + "subType": "06" + } + } + }, + "gcp_binData=04_rand_auto_altname": { + "kms": "gcp", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAFpiu9Q3LTuPmgdWBqo5Kw0vGF9xU1rMyE4xwR8GccZ7ZMrUcR4AnZnAP7ah5Oz8e7qonNYX4d09obesYSLlIjyK7J7qg+GWiEURgbvmOngaA=", + "subType": "06" + } + } + }, + "gcp_binData=04_rand_explicit_id": { + "kms": "gcp", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAFHRy8dveGuMng9WMmadIp39jD7iEfl3bEjKmzyNoAc0wIcSJZo9kdGbNEwZ4p+A1gz273fmAt/AJwAxwvqdlanLWBr4wiSKz1Mu9VaBcTlyY=", + "subType": "06" + } + } + }, + "gcp_binData=04_rand_explicit_altname": { + "kms": "gcp", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAFiqO+sKodqXuVox0zTbKuY4Ng0QE1If2hDLWXljAEZdYABPk20UJyL/CHR49WP2Cwvi4evJCf8sEfKpR+ugPiyxWzP3iVe6qqTzP93BBjqoc=", + "subType": "06" + } + } + }, + "gcp_binData=04_det_auto_id": { + "kms": "gcp", + "type": "binData=04", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAFEp5Gut6iENHUqDMVdBm4cxQy35gnslTf7vSWW9InFh323BvaTTiubxbxTiMKIa/u47MfMprL9HNQSwgpAQc4lped+YnlRW8RYvTcG4frFtA=", + "subType": "06" + } + } + }, + "gcp_binData=04_det_explicit_id": { + "kms": "gcp", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAFEp5Gut6iENHUqDMVdBm4cxQy35gnslTf7vSWW9InFh323BvaTTiubxbxTiMKIa/u47MfMprL9HNQSwgpAQc4lped+YnlRW8RYvTcG4frFtA=", + "subType": "06" + } + } + }, + "gcp_binData=04_det_explicit_altname": { + "kms": "gcp", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAFEp5Gut6iENHUqDMVdBm4cxQy35gnslTf7vSWW9InFh323BvaTTiubxbxTiMKIa/u47MfMprL9HNQSwgpAQc4lped+YnlRW8RYvTcG4frFtA=", + "subType": "06" + } + } + }, + "gcp_undefined_rand_explicit_id": { + "kms": "gcp", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "gcp_undefined_rand_explicit_altname": { + "kms": "gcp", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "gcp_undefined_det_explicit_id": { + "kms": "gcp", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "gcp_undefined_det_explicit_altname": { + "kms": "gcp", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "gcp_objectId_rand_auto_id": { + "kms": "gcp", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAH8Kt6coc8bPI4QIwS1tIdk6pPA05xlZvrOyAQgvoqaozMtWzG15OunQLDdS3yJ5WRiV7kO6CIKqRrvL2RykB5sw==", + "subType": "06" + } + } + }, + "gcp_objectId_rand_auto_altname": { + "kms": "gcp", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAHU5Yzmz2mbgNQrGSvglgVuv14nQWzipBkZUVSO4eYZ7wLrj/9t0fnizsu7Isgg5oA9fV0Snh/A9pDnHZWoccXUw==", + "subType": "06" + } + } + }, + "gcp_objectId_rand_explicit_id": { + "kms": "gcp", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAHsdq5/FLqbjMDiNzf+6k9yxUtFVjS/xSqErqaboOl21934pAzgkOzBGodpKKFuK0Ta4f3h21XS+84wlIYPMlTtw==", + "subType": "06" + } + } + }, + "gcp_objectId_rand_explicit_altname": { + "kms": "gcp", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAHokIdXxNQ/NBMdMAVNxyVuz/J5pMMdtfxxJxr7PbsRJ3FoD2QNjTgE1Wsz0G4o09Wv9UWD+/mIqPVlLgx1sRtPw==", + "subType": "06" + } + } + }, + "gcp_objectId_det_auto_id": { + "kms": "gcp", + "type": "objectId", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAHkcbaj3Hy3b4HkjRkMgiw5h6jBW7Sc56QSJmAPmVSc2T4B8d79A49dW0RyEiInZJcnVRjrYzUTRtgRaG4/FRd8g==", + "subType": "06" + } + } + }, + "gcp_objectId_det_explicit_id": { + "kms": "gcp", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAHkcbaj3Hy3b4HkjRkMgiw5h6jBW7Sc56QSJmAPmVSc2T4B8d79A49dW0RyEiInZJcnVRjrYzUTRtgRaG4/FRd8g==", + "subType": "06" + } + } + }, + "gcp_objectId_det_explicit_altname": { + "kms": "gcp", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAHkcbaj3Hy3b4HkjRkMgiw5h6jBW7Sc56QSJmAPmVSc2T4B8d79A49dW0RyEiInZJcnVRjrYzUTRtgRaG4/FRd8g==", + "subType": "06" + } + } + }, + "gcp_bool_rand_auto_id": { + "kms": "gcp", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAIf7vUYS5XFrEU4g03lzj9dk8a2MkaQdlH8nE/507D2Gm5XKQLi2jCENZ9UaQm3MQtVr4Uqrgz2GZiQHt9mXcG3w==", + "subType": "06" + } + } + }, + "gcp_bool_rand_auto_altname": { + "kms": "gcp", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAIdOC4Tx/TaVLRtOL/Qh8RUFIzHFB6nSegZoITwZeDethd8V3+R+aIAgzfN3pvmZzagHyVCm2nbNYJNdjOJhuDrg==", + "subType": "06" + } + } + }, + "gcp_bool_rand_explicit_id": { + "kms": "gcp", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAIzB14mX2vaZdiW9kGc+wYEgTCXA0FB5AVEyuERD00+K7U5Otlc6ZUwMtb9nGUu+M7PnnfxiDFHCrUWrTkAZzSUw==", + "subType": "06" + } + } + }, + "gcp_bool_rand_explicit_altname": { + "kms": "gcp", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAIhRLg79ACCMfeERBgG1wirirrZXZzbK11RxHkAbf14Fji2L3sdMBdLBU5I028+rmtDdC7khcNMt11V6XGKpAjnA==", + "subType": "06" + } + } + }, + "gcp_bool_det_explicit_id": { + "kms": "gcp", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": true + }, + "gcp_bool_det_explicit_altname": { + "kms": "gcp", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": true + }, + "gcp_date_rand_auto_id": { + "kms": "gcp", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAJL+mjI8xBmSahOOi3XkGRGxjhGNdJb445KZtRAaUdCV0vMKbrefuiDHJDPCYo7mLYNhRSIhQfs63IFYMrlKP26A==", + "subType": "06" + } + } + }, + "gcp_date_rand_auto_altname": { + "kms": "gcp", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAJbeyqO5FRmqvPYyOb0tdKtK6JOg8QKbCl37/iFeEm7N0T0Pjb8Io4U0ndB3O6fjokc3kDQrZcQkV+OFWIMuKFjw==", + "subType": "06" + } + } + }, + "gcp_date_rand_explicit_id": { + "kms": "gcp", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAJVz3rSYIcoYtM0tZ8pB2Ytgh8RvYPeZvW7aUVJfZkZlIhfUHOHEf5kHqxzt8E1l2n3lmK/7ZVCFUuCCmr8cZyWw==", + "subType": "06" + } + } + }, + "gcp_date_rand_explicit_altname": { + "kms": "gcp", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAJAiQqNyUcpuDEpFt7skp2NSHFCux2XObrIIFgXReYgtWoapL/n4zksJXl89PGavzNPBZbzgEa8uwwAe+S+Y6TLg==", + "subType": "06" + } + } + }, + "gcp_date_det_auto_id": { + "kms": "gcp", + "type": "date", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAJmATV2A1P5DmrS8uES6AMD9y+EU3x7u4K4J0p296iSkCEgIdZZORhPIEnuJK3FHw1II6IEShW2nd7sOJRZSGKcg==", + "subType": "06" + } + } + }, + "gcp_date_det_explicit_id": { + "kms": "gcp", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAJmATV2A1P5DmrS8uES6AMD9y+EU3x7u4K4J0p296iSkCEgIdZZORhPIEnuJK3FHw1II6IEShW2nd7sOJRZSGKcg==", + "subType": "06" + } + } + }, + "gcp_date_det_explicit_altname": { + "kms": "gcp", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAJmATV2A1P5DmrS8uES6AMD9y+EU3x7u4K4J0p296iSkCEgIdZZORhPIEnuJK3FHw1II6IEShW2nd7sOJRZSGKcg==", + "subType": "06" + } + } + }, + "gcp_null_rand_explicit_id": { + "kms": "gcp", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "gcp_null_rand_explicit_altname": { + "kms": "gcp", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "gcp_null_det_explicit_id": { + "kms": "gcp", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "gcp_null_det_explicit_altname": { + "kms": "gcp", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "gcp_regex_rand_auto_id": { + "kms": "gcp", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAALiebb3hWwJRqlgVEhLYKKvo6cnlU7BFnZnvlZ8GuIr11fUvcnS9Tg2m7vPmfL7WVyuNrXlR48x28Es49YuaxuIg==", + "subType": "06" + } + } + }, + "gcp_regex_rand_auto_altname": { + "kms": "gcp", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAALouDFNLVgBXqhJvBRj9DKacuD1AQ2NAVDW93P9NpZDFFwGOFxmKUcklbPj8KkHqvma8ovVUBTLLUDR+tKFRvC2Q==", + "subType": "06" + } + } + }, + "gcp_regex_rand_explicit_id": { + "kms": "gcp", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAALtdcT9+3R1he4eniT+1opqs/YtujFlqzBXssv+hCKhJQVY/IXde32nNpQ1WTgUc7jfIJl/v9HvuA9cDHPtDWWTg==", + "subType": "06" + } + } + }, + "gcp_regex_rand_explicit_altname": { + "kms": "gcp", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAALAwlRAlj4Zpn+wu9eOcs5CsNgrkVwrgmu1tc4wyQp0Lt+3UcplYsXQMrMPcTx3yB0JcI4Kh65n/DrAaA+G/a6iw==", + "subType": "06" + } + } + }, + "gcp_regex_det_auto_id": { + "kms": "gcp", + "type": "regex", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAALbCutQ7D94gk0djewcQiEdMFVVa21+Dn5enQf/mqPi3o7vPy7OejDBk9fiZRffsioRMhlx2cxqa8T3+AkeN96yg==", + "subType": "06" + } + } + }, + "gcp_regex_det_explicit_id": { + "kms": "gcp", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAALbCutQ7D94gk0djewcQiEdMFVVa21+Dn5enQf/mqPi3o7vPy7OejDBk9fiZRffsioRMhlx2cxqa8T3+AkeN96yg==", + "subType": "06" + } + } + }, + "gcp_regex_det_explicit_altname": { + "kms": "gcp", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAALbCutQ7D94gk0djewcQiEdMFVVa21+Dn5enQf/mqPi3o7vPy7OejDBk9fiZRffsioRMhlx2cxqa8T3+AkeN96yg==", + "subType": "06" + } + } + }, + "gcp_dbPointer_rand_auto_id": { + "kms": "gcp", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAMG8P+Y2YNIgknxE0/yPDCHASBvCU1IJwsEyaJPuOjn03enxEN7z/wbjVMN0lGUptDP3SVL+OIZtQ35VRP84MtnbdhcfZWqMhLjzrCjmtHUEg=", + "subType": "06" + } + } + }, + "gcp_dbPointer_rand_auto_altname": { + "kms": "gcp", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAMKCLFUN6ApB5fSVEWazRddhKTEwgqI/mxfe0BBxht69pZQYhTjhOJP0YcIrtr+RCeHOa4FIJgQod1CFOellIzO5YH5CuV4wPxCAlOdbJcBK8=", + "subType": "06" + } + } + }, + "gcp_dbPointer_rand_explicit_id": { + "kms": "gcp", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAM7ULEA6uKKv4Pu4Sa3aAt7dXtEwfQC98aJoLBapHT+xXtn5GWPynOZQNtV3lGaYExQjiGdYbzOcav3SVy/sYTe3ktgkQnuZfe0tk0zyvKIMM=", + "subType": "06" + } + } + }, + "gcp_dbPointer_rand_explicit_altname": { + "kms": "gcp", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAMoMveHO1MadAKuT498xiKWWBUKRbH7k7P2YETDg/BufVw0swos07rk6WJa1vqyF61QEmACjy4pmlK/5P0VfKJBAIvif51YqHPQkobJVS3nVA=", + "subType": "06" + } + } + }, + "gcp_dbPointer_det_auto_id": { + "kms": "gcp", + "type": "dbPointer", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAMz+9m1bE+Th9YeyPmJdtJPO0F5QYsGYtU/Eom/LSoYjDmTmV2ehkKx/cevIxJfZUc+Mvv/uGoeuubGl8tiX4l+f6yLrSIS6QBtIHYKXk+JNE=", + "subType": "06" + } + } + }, + "gcp_dbPointer_det_explicit_id": { + "kms": "gcp", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAMz+9m1bE+Th9YeyPmJdtJPO0F5QYsGYtU/Eom/LSoYjDmTmV2ehkKx/cevIxJfZUc+Mvv/uGoeuubGl8tiX4l+f6yLrSIS6QBtIHYKXk+JNE=", + "subType": "06" + } + } + }, + "gcp_dbPointer_det_explicit_altname": { + "kms": "gcp", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAMz+9m1bE+Th9YeyPmJdtJPO0F5QYsGYtU/Eom/LSoYjDmTmV2ehkKx/cevIxJfZUc+Mvv/uGoeuubGl8tiX4l+f6yLrSIS6QBtIHYKXk+JNE=", + "subType": "06" + } + } + }, + "gcp_javascript_rand_auto_id": { + "kms": "gcp", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAANqBD0ITMn4BaFnDp7BX7vXbRBkFwmjQRVUeBbwsQtv5WVlJMAd/2+w7tyH8Wc44x0/9U/DA5GVhpTrtdDyPBI3w==", + "subType": "06" + } + } + }, + "gcp_javascript_rand_auto_altname": { + "kms": "gcp", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAANtA0q4mbkAaKX4x1xk0/094Mln0wnh2bYnI6s6dh+l2WLDH7A9JMZxCl6kc4uOsEfbOvjP/PLIYtdMGs14EjM5A==", + "subType": "06" + } + } + }, + "gcp_javascript_rand_explicit_id": { + "kms": "gcp", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAANfrW3pmeiFdBFt5tJS6Auq9Wo/J4r/vMRiueLWxig5S1zYuf9kFPJMK/nN9HqQPIcBIJIC2i/uEPgeepaNXACCw==", + "subType": "06" + } + } + }, + "gcp_javascript_rand_explicit_altname": { + "kms": "gcp", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAANL7UZNzpwfwhRn/HflWIE9CSxGYNwLSo9d86HsOJ42rrZKq6HQqm/hiEAg0lyqCxVIVFxYEc2BUWSaq4/+SSyZw==", + "subType": "06" + } + } + }, + "gcp_javascript_det_auto_id": { + "kms": "gcp", + "type": "javascript", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAANB2d97R8nUJqnG0JPsWzyFe5pct5jvUljdkPnlZvLN1ZH+wSu4WmLfjri6IzzYP//f8tywn4Il+R4lZ0Kr/RAeA==", + "subType": "06" + } + } + }, + "gcp_javascript_det_explicit_id": { + "kms": "gcp", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAANB2d97R8nUJqnG0JPsWzyFe5pct5jvUljdkPnlZvLN1ZH+wSu4WmLfjri6IzzYP//f8tywn4Il+R4lZ0Kr/RAeA==", + "subType": "06" + } + } + }, + "gcp_javascript_det_explicit_altname": { + "kms": "gcp", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAANB2d97R8nUJqnG0JPsWzyFe5pct5jvUljdkPnlZvLN1ZH+wSu4WmLfjri6IzzYP//f8tywn4Il+R4lZ0Kr/RAeA==", + "subType": "06" + } + } + }, + "gcp_symbol_rand_auto_id": { + "kms": "gcp", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAOsGdnr6EKcBdOAvYrP0o1pWbhhJbYsqfVwwwS1zq6ZkBayOss2J3TuYwBGXhJFlq3iIiWLdxGQ883XIvuAECnqUNuvpK2rOLwtDg8xJLiH24=", + "subType": "06" + } + } + }, + "gcp_symbol_rand_auto_altname": { + "kms": "gcp", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAOpfa6CUSnJBvnWdd7pSZ2pXAbYm68Yka6xa/fuyhVx/Tc926/JpqmOmQtXqbOj8dZra0rQ3/yxHySwgD7s9Qr+xvyL7LvAguGkGmEV5H4Xz4=", + "subType": "06" + } + } + }, + "gcp_symbol_rand_explicit_id": { + "kms": "gcp", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAO085iqYGFdtjiFWHcNqE0HuKMNHmk49DVh+pX8Pb4p3ehB57JL1nRqaXqHPqhFenxSEInT/te9HQRr+ADcHADvUGsScfm/n85v85nq6X+5y4=", + "subType": "06" + } + } + }, + "gcp_symbol_rand_explicit_altname": { + "kms": "gcp", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAOiidb+2TsbAb2wc7MtDzb/UYsjgVNSw410Sz9pm+Uy7aZROE5SURKXdLjrCH2ZM2a+XCAl3o9yAoNgmAjEvYVxjmyzLK00EVjT42MBOrdA+k=", + "subType": "06" + } + } + }, + "gcp_symbol_det_auto_id": { + "kms": "gcp", + "type": "symbol", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAOFBGo77joqvZl7QQMB9ebMsAI3uro8ILQTJsTUgAqNzSh1mNzqihGHZYe84xtgMrVxNuwcjkidkRbNnLXWLuarOx4tgmOLx5A5G1eYEe3s7Q=", + "subType": "06" + } + } + }, + "gcp_symbol_det_explicit_id": { + "kms": "gcp", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAOFBGo77joqvZl7QQMB9ebMsAI3uro8ILQTJsTUgAqNzSh1mNzqihGHZYe84xtgMrVxNuwcjkidkRbNnLXWLuarOx4tgmOLx5A5G1eYEe3s7Q=", + "subType": "06" + } + } + }, + "gcp_symbol_det_explicit_altname": { + "kms": "gcp", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAOFBGo77joqvZl7QQMB9ebMsAI3uro8ILQTJsTUgAqNzSh1mNzqihGHZYe84xtgMrVxNuwcjkidkRbNnLXWLuarOx4tgmOLx5A5G1eYEe3s7Q=", + "subType": "06" + } + } + }, + "gcp_javascriptWithScope_rand_auto_id": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAPUsQHeXWhdmyfQ2Sq1ev1HMuMhBTc/FZFKO9tMMcI9qzjr+z4IdCOFCcx24/T/6NCsDpMiOGNnCdaBCCNRwNM0CTIkpHNLO+RSZORDgAsm9Q=", + "subType": "06" + } + } + }, + "gcp_javascriptWithScope_rand_auto_altname": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAPRZawtuu0gErebyFqiQw0LxniWhdeujGzaqfAXriGo/2fU7PalzTlWQa8wsv0y7Q/i1K4JbQwCEFpJWLppmtZshCGbVWjpPljB2BH4NNrLPE=", + "subType": "06" + } + } + }, + "gcp_javascriptWithScope_rand_explicit_id": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAP0qkQjuKmKIqdrsrR9djxt+1jFlEL7K9bP1oz7QWuY38dZJOoGwa6G1bP4wDzjsucJLCEgU2IY+t7BHraBFXvR/Aar8ID5eXcvJ7iOPIyqUw=", + "subType": "06" + } + } + }, + "gcp_javascriptWithScope_rand_explicit_altname": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAP6L41iuBWGLg3hQZuhXp4MupTQvIT07+/+CRY292sC02mehk5BkuSOEVrehlvyvBJFKia4Bqd/UWvY8PnUPLqFKTLnokONWbAuh36y3gjStw=", + "subType": "06" + } + } + }, + "gcp_javascriptWithScope_det_explicit_id": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "gcp_javascriptWithScope_det_explicit_altname": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "gcp_int_rand_auto_id": { + "kms": "gcp", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAQ+6oRKWMSvC+3UGrHSyGeVlR9bFnZtFTmYlUoGn04k6ndtCl8rsmBVUV6dMMYd7znnZtTSIGPI8q6jwf/NJjdIw==", + "subType": "06" + } + } + }, + "gcp_int_rand_auto_altname": { + "kms": "gcp", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAQnz5jAbrrdutTPFA4m3MvlVJr3bpurTKY5xjwO5k8DZpeWTJzr+kVEJjG6M8/RgC/0UFNgBBrDbDhYa8PZHRijw==", + "subType": "06" + } + } + }, + "gcp_int_rand_explicit_id": { + "kms": "gcp", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAQfRFoxUgjrv8up/eZ/fLlr/z++d/jFm30nYvKqsnQT7vkmmujJWc8yAtthR9OI6W5biBgAkounqRHhvatLZC6gA==", + "subType": "06" + } + } + }, + "gcp_int_rand_explicit_altname": { + "kms": "gcp", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAQY/ePk59RY6vLejx9a5ITwkT9000KAubVSqMoQwv7lNXO+GKZfZoLHG6k1MA/IxTvl1Zbz1Tw1bTctmj0HPEGNA==", + "subType": "06" + } + } + }, + "gcp_int_det_auto_id": { + "kms": "gcp", + "type": "int", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAQE9RVV9pOuysUUEGKq0u6ztFM0gTpoOHcHsTFQstA7+L9XTvxWEgL3RgNeq5KtKdODlxl62niV8dnQwlSoDSSWw==", + "subType": "06" + } + } + }, + "gcp_int_det_explicit_id": { + "kms": "gcp", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAQE9RVV9pOuysUUEGKq0u6ztFM0gTpoOHcHsTFQstA7+L9XTvxWEgL3RgNeq5KtKdODlxl62niV8dnQwlSoDSSWw==", + "subType": "06" + } + } + }, + "gcp_int_det_explicit_altname": { + "kms": "gcp", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAQE9RVV9pOuysUUEGKq0u6ztFM0gTpoOHcHsTFQstA7+L9XTvxWEgL3RgNeq5KtKdODlxl62niV8dnQwlSoDSSWw==", + "subType": "06" + } + } + }, + "gcp_timestamp_rand_auto_id": { + "kms": "gcp", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAARLnk1LpJIriKr6iiY1yBDGnfkRaHNwWcQyL+mORtYC4+AQ6oMv0qpGrJxS2QCbYY1tGmAISqZHCIExCG+TIv4bw==", + "subType": "06" + } + } + }, + "gcp_timestamp_rand_auto_altname": { + "kms": "gcp", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAARaqYXh9AVZI6gvRZrBwbprE5P3K5Qf4PIK1ca+mLRNOof0EExyAhtku7mYXusLeq0ww/tV6Zt1cA36KsT8a0Nog==", + "subType": "06" + } + } + }, + "gcp_timestamp_rand_explicit_id": { + "kms": "gcp", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAARLXzBjkCN8BpfXDIrb94kuZCD07Uo/DMBfMIWQtAb1++tTheUoY2ClQz33Luh4g8NXwuMJ7h8ufE70N2+b1yrUg==", + "subType": "06" + } + } + }, + "gcp_timestamp_rand_explicit_altname": { + "kms": "gcp", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAARe44QH9ZvTAuHsWhEMoue8eHod+cJpBm+Kl/Xtw7NI/6UTOOHC5Kkg20EvX3+GwXdAGk0bUSCFiTZb/yPox1OlA==", + "subType": "06" + } + } + }, + "gcp_timestamp_det_auto_id": { + "kms": "gcp", + "type": "timestamp", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAARzXjP6d6j/iQxiz1/TC/m+IfAGLFH9wY2ksS//i9x15QttlhcRrT3XmPvxaP5OjTHac4Gq3m2aXiJH56lETyl8A==", + "subType": "06" + } + } + }, + "gcp_timestamp_det_explicit_id": { + "kms": "gcp", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAARzXjP6d6j/iQxiz1/TC/m+IfAGLFH9wY2ksS//i9x15QttlhcRrT3XmPvxaP5OjTHac4Gq3m2aXiJH56lETyl8A==", + "subType": "06" + } + } + }, + "gcp_timestamp_det_explicit_altname": { + "kms": "gcp", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAARzXjP6d6j/iQxiz1/TC/m+IfAGLFH9wY2ksS//i9x15QttlhcRrT3XmPvxaP5OjTHac4Gq3m2aXiJH56lETyl8A==", + "subType": "06" + } + } + }, + "gcp_long_rand_auto_id": { + "kms": "gcp", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAASuGZs48eEyVBJ9vvM6cvRySfuR0WM4kL7lx52rSGXBKtkZywyP5rJwNtRn9WTBMDqc1O/4jUgYXpqHx39SLhUPA==", + "subType": "06" + } + } + }, + "gcp_long_rand_auto_altname": { + "kms": "gcp", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAS/62F71oKTX1GlvOP89uNhXpIyLZ5OdnuLeM/hvL5HWyOudSb06cG3+xnPg3QgppAYFK5X2PGgrEcrA87AykLPg==", + "subType": "06" + } + } + }, + "gcp_long_rand_explicit_id": { + "kms": "gcp", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAASSgx+p4YzTvjZ+GCZCFHEKHNXJUSloPnLRHE4iJ515Epb8Tox7h8/aIAkB3ulnDS9BiT5UKdye2TWf8OBEwkXzg==", + "subType": "06" + } + } + }, + "gcp_long_rand_explicit_altname": { + "kms": "gcp", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAAStqszyEfltpgd3aYeoyqaJX27OX861o06VhNX/N2fdSfKx0NQq/hWlWTkX6hK3hjCijiTtHmhFQR6QLkHD/6THw==", + "subType": "06" + } + } + }, + "gcp_long_det_auto_id": { + "kms": "gcp", + "type": "long", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAS0wJHtZKnxJlWnlSu0xuq7bZR25UdwcbdCRSaXBC0EXEFuqlzrZSn1lcwKPKGZQO8EQ6SdQDqK95alMLmM8eQrQ==", + "subType": "06" + } + } + }, + "gcp_long_det_explicit_id": { + "kms": "gcp", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAS0wJHtZKnxJlWnlSu0xuq7bZR25UdwcbdCRSaXBC0EXEFuqlzrZSn1lcwKPKGZQO8EQ6SdQDqK95alMLmM8eQrQ==", + "subType": "06" + } + } + }, + "gcp_long_det_explicit_altname": { + "kms": "gcp", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ARgjwAAAAAAAAAAAAAAAAAAS0wJHtZKnxJlWnlSu0xuq7bZR25UdwcbdCRSaXBC0EXEFuqlzrZSn1lcwKPKGZQO8EQ6SdQDqK95alMLmM8eQrQ==", + "subType": "06" + } + } + }, + "gcp_decimal_rand_auto_id": { + "kms": "gcp", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAATg4U3nbHBX/Az3ie2yurEIJO6cFryQWKiCpBbx1z0NF7RXd7kFC1XzaY6zcBjfl2AfRO8FFmgjTmFXb6gTRSSF0iAZJZTslfe3n6YFtwSKDI=", + "subType": "06" + } + } + }, + "gcp_decimal_rand_auto_altname": { + "kms": "gcp", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAATdSSyp0ewboV5zI3T3TV/FOrdx0UQbFHhqcH+yqpotoWPSw5dxE+BEoihYLeaPKuVU/rUIY4TUv05Egj7Ovg62Kpk3cPscxsGtE/T2Ppbt6o=", + "subType": "06" + } + } + }, + "gcp_decimal_rand_explicit_id": { + "kms": "gcp", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAATl7k20T22pf5Y9knVwIDyOIlbHyZBJqyi3Mai8APEZIYjpSKDKs8QNAH69CIjupyge8Izw4Cuch0bRrvMbp6YFfrUgk1JIQ4iLKkqqzHpBTY=", + "subType": "06" + } + } + }, + "gcp_decimal_rand_explicit_altname": { + "kms": "gcp", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AhgjwAAAAAAAAAAAAAAAAAATF7YLkhkuLhXdxrQk2fJTs128tRNYHeodkqw7ha/TxW3Czr5gE272gnkdzfNoS7uu9XwOr1yjrC6y/8gHALAWn77WvGrAlBktLQbIIinsuds=", + "subType": "06" + } + } + }, + "gcp_decimal_det_explicit_id": { + "kms": "gcp", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "gcp_decimal_det_explicit_altname": { + "kms": "gcp", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "gcp_minKey_rand_explicit_id": { + "kms": "gcp", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "gcp_minKey_rand_explicit_altname": { + "kms": "gcp", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "gcp_minKey_det_explicit_id": { + "kms": "gcp", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "gcp_minKey_det_explicit_altname": { + "kms": "gcp", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "gcp_maxKey_rand_explicit_id": { + "kms": "gcp", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "gcp_maxKey_rand_explicit_altname": { + "kms": "gcp", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "gcp_maxKey_det_explicit_id": { + "kms": "gcp", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "gcp_maxKey_det_explicit_altname": { + "kms": "gcp", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "kmip_double_rand_auto_id": { + "kms": "kmip", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAB1hL/nPkpQtqxQUANbIJr30PQ98vPvaoy4JWUoElOL+cCnrSra3o7W+12dydy0rCS2EKrVm7Fw0C8L9nf1hpWjw==", + "subType": "06" + } + } + }, + "kmip_double_rand_auto_altname": { + "kms": "kmip", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAABxlcphy2SxXlkRBvO1Z3nNUqchmeOhIhkdYBbbW7CwYeLVRDciXFsZN73Nb9Bm+W4IpUNpo6mqFEtfjevIjtFyg==", + "subType": "06" + } + } + }, + "kmip_double_rand_explicit_id": { + "kms": "kmip", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAABx5AfRSiblFc1DGwxRIaUSP2kaM76ryzPUKL9KnEgnX1kjIlFz5B15uMht2cxdrntHFe1qZZk8V9PxTBpWZhJ8Q==", + "subType": "06" + } + } + }, + "kmip_double_rand_explicit_altname": { + "kms": "kmip", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAABXUC9v9HPrmU9tINzFmr2sQM9f7GHDus+y5T4pWX28PRtfnTysN/ANCfB9RosoR/wuKsbznwwD2JfSzOvlKo3PQ==", + "subType": "06" + } + } + }, + "kmip_double_det_explicit_id": { + "kms": "kmip", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDouble": "1.2339999999999999858" + } + }, + "kmip_double_det_explicit_altname": { + "kms": "kmip", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDouble": "1.2339999999999999858" + } + }, + "kmip_string_rand_auto_id": { + "kms": "kmip", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAACGHmqW1qbfqVlfB0x0CkXCk9smhs3yXsxJ/8eypSgbDQqVLSW2nf5bbHpnoCHHNtQ7I7ZBXzPzDLH2GgMJpopeQ==", + "subType": "06" + } + } + }, + "kmip_string_rand_auto_altname": { + "kms": "kmip", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAC9BJTD1pEMbslAjbJYt7yx/jzKkcZF3axu96+NYwp8afUCjXG5TOUZzODOwkbJuWgr7DBxa2GkZTvaAEk86h+Ow==", + "subType": "06" + } + } + }, + "kmip_string_rand_explicit_id": { + "kms": "kmip", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAACQlG28ECy8KHXC7GEPdC8+raBo2RMJwl5pofcPaTGkPUEbkreguMd1mYctNb90vXxby1nNeJY4o5zJJCMiNhNXg==", + "subType": "06" + } + } + }, + "kmip_string_rand_explicit_altname": { + "kms": "kmip", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAACbWuK+3nzeKSNVjmgHb0Ii7rA+CsAd+gYubPiMiHXZwE/o6i9FYWN+t/VK3p4K0CwIi6q3cycrMb2IgcvM27Q7Q==", + "subType": "06" + } + } + }, + "kmip_string_det_auto_id": { + "kms": "kmip", + "type": "string", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAC5OZgr9keCXOIj5Fi06i4win1xt7gpsyPA4Os+HdFn1MIP9tnktvWNRb8Rqhuj2O9KO83brx74Hu3EQ4nT6uCMw==", + "subType": "06" + } + } + }, + "kmip_string_det_explicit_id": { + "kms": "kmip", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAC5OZgr9keCXOIj5Fi06i4win1xt7gpsyPA4Os+HdFn1MIP9tnktvWNRb8Rqhuj2O9KO83brx74Hu3EQ4nT6uCMw==", + "subType": "06" + } + } + }, + "kmip_string_det_explicit_altname": { + "kms": "kmip", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAC5OZgr9keCXOIj5Fi06i4win1xt7gpsyPA4Os+HdFn1MIP9tnktvWNRb8Rqhuj2O9KO83brx74Hu3EQ4nT6uCMw==", + "subType": "06" + } + } + }, + "kmip_object_rand_auto_id": { + "kms": "kmip", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAADh2nGqaAUwHDRVjqYpj8JAPH7scmiHp1Z9SGBZQ6Fapxm+zWDdTBHyitM9U69BctJ5DaaafyqFOj5yr6sJ+ebJQ==", + "subType": "06" + } + } + }, + "kmip_object_rand_auto_altname": { + "kms": "kmip", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAD1YhOKyNle4y0Qbeio1HlCULLeTCALCLgKSITd50bilD+oDyqQawixJAwphcdjhLdFzbFwst5RWqpsiWMPHx4hQ==", + "subType": "06" + } + } + }, + "kmip_object_rand_explicit_id": { + "kms": "kmip", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAADveILoWFgX7AhUWCv8UL52TUa75qHuoNadnTQydJlqd6PVmtRKj+8vS7VwxNWPaH4wB1Tk7emMyFEbZpvvzjxqQ==", + "subType": "06" + } + } + }, + "kmip_object_rand_explicit_altname": { + "kms": "kmip", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAADB/LN9V/4SROJn+ESHRLM7wwcUltQUx3+LbbYXjPDXiiV14HK76Iyy6ZxJ+M5qC9bRj3afhTKuWLBblB8WwksOg==", + "subType": "06" + } + } + }, + "kmip_object_det_explicit_id": { + "kms": "kmip", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "kmip_object_det_explicit_altname": { + "kms": "kmip", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "kmip_array_rand_auto_id": { + "kms": "kmip", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAEasWXQam8XtOkSO0nEttMCQ0iZ4V8DDmhMKyQDFDsiNHyF2h98Ya/xFv4ZSlbpGWXPBvBATEGgov/PDg2vhVi53y4Pk33RHfY60hABuksp3o=", + "subType": "06" + } + } + }, + "kmip_array_rand_auto_altname": { + "kms": "kmip", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAEj3A1DYSEHm/3SlEmusA+pewxRPUoZ2NAjs60ioEBlCw9n6yiiB+X8d/w40TKsjZcOSfh05NC0z3gnpqQvrNolkxkvi9dmFiZeiiv5vBZUPI=", + "subType": "06" + } + } + }, + "kmip_array_rand_explicit_id": { + "kms": "kmip", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAEqeJW+L6lP0bn5QcD0FMI0C8vv2n5kV7SKgqKi1o5mxaxmp3Cjlspf7yumfSiQ5js6G9yJVAvHuxlqv14UFyR9RgXS0PIA8WzsAqkL0sJSw0=", + "subType": "06" + } + } + }, + "kmip_array_rand_explicit_altname": { + "kms": "kmip", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAEnPlPwy0B1VKuNum1GzkZwQjZia5jNYL5bf/k+PbfhnToTRWGxx8+E3R7XXp6YT/rFkjPlzU8ww9+iZNo2oqNpYuHdrIC8ybhO6HZAlvcERo=", + "subType": "06" + } + } + }, + "kmip_array_det_explicit_id": { + "kms": "kmip", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "kmip_array_det_explicit_altname": { + "kms": "kmip", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "kmip_binData=00_rand_auto_id": { + "kms": "kmip", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAFliNDZ6DmjoVcYQBCKDI9njpBsDELg+TD6XLF7xbZnMaJCCHLHr7w3x2/xFfrFSN44CtGAKOniYPCMAspaxHqOA==", + "subType": "06" + } + } + }, + "kmip_binData=00_rand_auto_altname": { + "kms": "kmip", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAF/P8LPmHKGgG0l5/Xi7jdkwfxpGPxoY0417suCvN6zjM3JNdufytzkektrm9CbBb1SnZCGYF9c0FCMzFG+tN/dg==", + "subType": "06" + } + } + }, + "kmip_binData=00_rand_explicit_id": { + "kms": "kmip", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAFWI0N4RbnYdEiFrzNpbRN9p+bSLm8Lthiu4K3/CvBg6GQpLMVQFhjW01Bud0lxpT2ohRnOK+ASUhiFcUU/t/lWQ==", + "subType": "06" + } + } + }, + "kmip_binData=00_rand_explicit_altname": { + "kms": "kmip", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAFQZvAtpY4cjEr1rJWVoUGaZKmzocSJ0muHose7Tk5kRDczjFa4Jcu4hN7JLM9qz2z4g+WJC3KQTdW4ZBXStke/Q==", + "subType": "06" + } + } + }, + "kmip_binData=00_det_auto_id": { + "kms": "kmip", + "type": "binData=00", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAFohIHrvzu8xLxVHsnYEDhZmv8BpEoEtFSjMUQzvBLUInvvTuU/rOzlVL88CkAEII7M3hcvrz8FKY7b7lC1veoYg==", + "subType": "06" + } + } + }, + "kmip_binData=00_det_explicit_id": { + "kms": "kmip", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAFohIHrvzu8xLxVHsnYEDhZmv8BpEoEtFSjMUQzvBLUInvvTuU/rOzlVL88CkAEII7M3hcvrz8FKY7b7lC1veoYg==", + "subType": "06" + } + } + }, + "kmip_binData=00_det_explicit_altname": { + "kms": "kmip", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAFohIHrvzu8xLxVHsnYEDhZmv8BpEoEtFSjMUQzvBLUInvvTuU/rOzlVL88CkAEII7M3hcvrz8FKY7b7lC1veoYg==", + "subType": "06" + } + } + }, + "kmip_binData=04_rand_auto_id": { + "kms": "kmip", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAFn7rhdO8tYq77uVxcqd9Qjz84Yg7JnJMYf0ULTMTh1vJHacckkhXw+8fIMMiAKwuOVwGkMAtu5RBvrFqdfxryCg8RLTxu1YYVthufiClEIS0=", + "subType": "06" + } + } + }, + "kmip_binData=04_rand_auto_altname": { + "kms": "kmip", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAFwwXQx9dKyoyHq7GBMmHzYe9ysoJK/f/ZWzA6nErau9MtX1gqi7VRsYqkamb47/zVbsLZwPMmdgNyPxEh3kqbV2D61t5RG2A3VeqhO1pTF8c=", + "subType": "06" + } + } + }, + "kmip_binData=04_rand_explicit_id": { + "kms": "kmip", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAFALeGeinJ8DE+WZniLdCIW2gfJUj445Ukp9PvRLgBXLGedl8mIXlLF2eu3BA9vP6s5y9w6peQjhn+oEofrsUVYD2duyzeIRMKgNiNchjf6TU=", + "subType": "06" + } + } + }, + "kmip_binData=04_rand_explicit_altname": { + "kms": "kmip", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAF06Fx8CO3OSKE3fGri0VwK0e22YiG9LH2QkDTsRdFbT2lBm+bDD9FrEY8vKWS5RljMuysaxjBOzZ98d2LEs6k8LMOm83Nz/RESe4ZbbcfdQ0=", + "subType": "06" + } + } + }, + "kmip_binData=04_det_auto_id": { + "kms": "kmip", + "type": "binData=04", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAFzmZI909fJgxOykJtvOlv5LsX8z6BxUX2Xg5TsIwOxJMPSC8usm/zR7sZawoVBOuJxtNVLY/8oNP/4pFtAmQo02bUOtTo1yxNz/IZa9x+Q5E=", + "subType": "06" + } + } + }, + "kmip_binData=04_det_explicit_id": { + "kms": "kmip", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAFzmZI909fJgxOykJtvOlv5LsX8z6BxUX2Xg5TsIwOxJMPSC8usm/zR7sZawoVBOuJxtNVLY/8oNP/4pFtAmQo02bUOtTo1yxNz/IZa9x+Q5E=", + "subType": "06" + } + } + }, + "kmip_binData=04_det_explicit_altname": { + "kms": "kmip", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAFzmZI909fJgxOykJtvOlv5LsX8z6BxUX2Xg5TsIwOxJMPSC8usm/zR7sZawoVBOuJxtNVLY/8oNP/4pFtAmQo02bUOtTo1yxNz/IZa9x+Q5E=", + "subType": "06" + } + } + }, + "kmip_undefined_rand_explicit_id": { + "kms": "kmip", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "kmip_undefined_rand_explicit_altname": { + "kms": "kmip", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "kmip_undefined_det_explicit_id": { + "kms": "kmip", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "kmip_undefined_det_explicit_altname": { + "kms": "kmip", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "kmip_objectId_rand_auto_id": { + "kms": "kmip", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAHZFzE908RuO5deEt3t2QQdT12ybwqbm8D+sMJrdKt2Wp4kVPsw4ocAGGsRYN6VXe46P5fmyG5HqVWn0hkflZnQg==", + "subType": "06" + } + } + }, + "kmip_objectId_rand_auto_altname": { + "kms": "kmip", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAH3dPKyCCStvOtVGzlgIS33fsl8OAwQblt9i21pOVuLiliY1Tup9EtkSic88+nNEtXnq9gRknRzLthXv/k1ql+7Q==", + "subType": "06" + } + } + }, + "kmip_objectId_rand_explicit_id": { + "kms": "kmip", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAHcEjxVfHDSfLzFxAuK/rs/Pn/XV7jLkgKXZYeY0PNlRi1MHojN2AvQqI3J2rOvAjuYfikGcpvGPp/goqUbV9HYw==", + "subType": "06" + } + } + }, + "kmip_objectId_rand_explicit_altname": { + "kms": "kmip", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAHX65sNHnRYpx3VbWPCdQyFe7u0Y5ItabLEduqDeVsPk/iK4X3GjCSHQfw1yPi+CA+/veVpgdonwws6RiYV4ZZ5Q==", + "subType": "06" + } + } + }, + "kmip_objectId_det_auto_id": { + "kms": "kmip", + "type": "objectId", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAHKU7mcdGEq2WGrDB6TicipLQstAk6G3PkiNt5F3bMavpKLjz04UBrd8aWGVG2gJTTON1UKRztiYFgRvb8f+LK/Q==", + "subType": "06" + } + } + }, + "kmip_objectId_det_explicit_id": { + "kms": "kmip", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAHKU7mcdGEq2WGrDB6TicipLQstAk6G3PkiNt5F3bMavpKLjz04UBrd8aWGVG2gJTTON1UKRztiYFgRvb8f+LK/Q==", + "subType": "06" + } + } + }, + "kmip_objectId_det_explicit_altname": { + "kms": "kmip", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAHKU7mcdGEq2WGrDB6TicipLQstAk6G3PkiNt5F3bMavpKLjz04UBrd8aWGVG2gJTTON1UKRztiYFgRvb8f+LK/Q==", + "subType": "06" + } + } + }, + "kmip_bool_rand_auto_id": { + "kms": "kmip", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAIw/xgJlKEvErmVtue3X3RFsOI2sttAbxnzh1INc9GUQ2vok1VwYt9k88RxMPiOwMAZG7P1MlAdx7zt865onPKOw==", + "subType": "06" + } + } + }, + "kmip_bool_rand_auto_altname": { + "kms": "kmip", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAIn8IuzlNHbpTgXOd1wEp364zJOBxj2Zf7a9B5osUV1sDY0G1OVpEnuDvZeUsdiUSyRjTTxzyuD/KZlKZ3+qrnrA==", + "subType": "06" + } + } + }, + "kmip_bool_rand_explicit_id": { + "kms": "kmip", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAI3Nz9PdjUYQRGfTtvYSR8EQuUKFL0wdlEdfSCTBmMBhBPuuF9KxqCgy+ldVu1DRRgg3346DOKEEtE9BJPPInJ6Q==", + "subType": "06" + } + } + }, + "kmip_bool_rand_explicit_altname": { + "kms": "kmip", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAIEGjqoerIZBk8Rw+YTO7jFKWzagDS8mEpD+9Wm1Q0r0ZHUmV0dQZcIqRV4oUk8U8uHUn0N3t2qGLr+rhUs4GH/g==", + "subType": "06" + } + } + }, + "kmip_bool_det_explicit_id": { + "kms": "kmip", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": true + }, + "kmip_bool_det_explicit_altname": { + "kms": "kmip", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": true + }, + "kmip_date_rand_auto_id": { + "kms": "kmip", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAJgr0v4xetUXjlLcPcyKv/rzjtWOKp9CZJcm23Noglu5RR/rXJS0qKI+W9MmJ64TMf27KvaJ0UXwfTRrvOC1plCg==", + "subType": "06" + } + } + }, + "kmip_date_rand_auto_altname": { + "kms": "kmip", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAJoeysAaiPsVK+JL1P1vD/9xF92m5kKidUdn6yklPlSKN4VVEBTymDetTLujULs1u1TlrS71jVLxo3xEwpG/KQvg==", + "subType": "06" + } + } + }, + "kmip_date_rand_explicit_id": { + "kms": "kmip", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAJVwu4+Su0DktpnZvzTBHYpWbWTq5gho/SLijrcIrFJcvq4YrjjPCXv+odCl95tkH+J1RlJdQ5Cr0umEIazLa6GA==", + "subType": "06" + } + } + }, + "kmip_date_rand_explicit_altname": { + "kms": "kmip", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAJWTYpjbDkIf82QXHMGrvd0SqhP8cBIakfYJf5aNcNrs86vxRhiG3KwETWPeOOlPZ6n1WjE2bOLB+DJTAxmJvahA==", + "subType": "06" + } + } + }, + "kmip_date_det_auto_id": { + "kms": "kmip", + "type": "date", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAJ/+sQrUqQh+JADSVIKM0d68gDUhDy37M1z1uvROzQw6hUAbQeD0DWdztADKg560UTPM4uOgH4NAyhLyBLMrWWHg==", + "subType": "06" + } + } + }, + "kmip_date_det_explicit_id": { + "kms": "kmip", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAJ/+sQrUqQh+JADSVIKM0d68gDUhDy37M1z1uvROzQw6hUAbQeD0DWdztADKg560UTPM4uOgH4NAyhLyBLMrWWHg==", + "subType": "06" + } + } + }, + "kmip_date_det_explicit_altname": { + "kms": "kmip", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAJ/+sQrUqQh+JADSVIKM0d68gDUhDy37M1z1uvROzQw6hUAbQeD0DWdztADKg560UTPM4uOgH4NAyhLyBLMrWWHg==", + "subType": "06" + } + } + }, + "kmip_null_rand_explicit_id": { + "kms": "kmip", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "kmip_null_rand_explicit_altname": { + "kms": "kmip", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "kmip_null_det_explicit_id": { + "kms": "kmip", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "kmip_null_det_explicit_altname": { + "kms": "kmip", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "kmip_regex_rand_auto_id": { + "kms": "kmip", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAALi8avMfpxSlDsSTqdxO8O2B1M79gOElyUIdXySQo7mvgHlf4oHQ7r94lL9dnsA2t/jmUmBKoGypaUQUSQE+9x+A==", + "subType": "06" + } + } + }, + "kmip_regex_rand_auto_altname": { + "kms": "kmip", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAALfHerZ/KolaBrb5qi3SpeNVW+i/nh5mkcdtQg5f1pHePr68KryHucM/XDAzbMqrPlag2/41STGYdJqzYO7Mbppg==", + "subType": "06" + } + } + }, + "kmip_regex_rand_explicit_id": { + "kms": "kmip", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAALOhKDVAN5cuDyB1EuRFWgKKt0wGJ63E5pPY8Tq2TXMNgCxUUc5O+TE+Ux4ls/uMyOBA3gPzND0CZKiru0i7ACUQ==", + "subType": "06" + } + } + }, + "kmip_regex_rand_explicit_altname": { + "kms": "kmip", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAALK3Hg8xX9gX+d3vKh7aosRP9CS2CIFeG9sapZv3OAPv1eWjY62Cp/G16kJ0BQt33RYD+DzD3gWupfUSyNZR0gng==", + "subType": "06" + } + } + }, + "kmip_regex_det_auto_id": { + "kms": "kmip", + "type": "regex", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAALaQXA8rItT7ELVxO8XtAWdHuiXFFPmnMhS5PMrUy/6mRtbq4fvU9dascW7ozonKOh8ad6+MIT7B/STv9dVBF4Kw==", + "subType": "06" + } + } + }, + "kmip_regex_det_explicit_id": { + "kms": "kmip", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAALaQXA8rItT7ELVxO8XtAWdHuiXFFPmnMhS5PMrUy/6mRtbq4fvU9dascW7ozonKOh8ad6+MIT7B/STv9dVBF4Kw==", + "subType": "06" + } + } + }, + "kmip_regex_det_explicit_altname": { + "kms": "kmip", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAALaQXA8rItT7ELVxO8XtAWdHuiXFFPmnMhS5PMrUy/6mRtbq4fvU9dascW7ozonKOh8ad6+MIT7B/STv9dVBF4Kw==", + "subType": "06" + } + } + }, + "kmip_dbPointer_rand_auto_id": { + "kms": "kmip", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAMoGkfmmUWTI+0aW7jVyCJ5Dgru1SCXBUmJSRzDL0D57pNruQ+79tVVcI6Uz5j87DhZFxShHbPjj583vLOOBNM3WGzZCpqH3serhHTWvXK+NM=", + "subType": "06" + } + } + }, + "kmip_dbPointer_rand_auto_altname": { + "kms": "kmip", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAMwu1WaRhhv43xgxLNxuenbND9M6mxGtCs9o4J5+yfL95XNB9Daie3RcLlyngz0pncBie6IqjhTycXsxTLQ94Jdg6m5GD5cU541LYKvhbv5f4=", + "subType": "06" + } + } + }, + "kmip_dbPointer_rand_explicit_id": { + "kms": "kmip", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAM+CIoCAisUwhhJtWQLolxQGQWafniwYyvaJQHmJC94Uwbf1gPfhMR42v2VtrmIVP0J0BaP/xf0cco2/qWRdKGZpgkK2CK6M972NtnZ/2x03A=", + "subType": "06" + } + } + }, + "kmip_dbPointer_rand_explicit_altname": { + "kms": "kmip", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAMjbeE9+EaJYjGfeAuxsV8teOdsW8bfnlkvji/tE11Zq89UMGx+oUsZzeLjUgVZ5nxsZKCZjEAq+DPnwFVC+MgqNeqWL7fRChODFlPGH2ZC+8=", + "subType": "06" + } + } + }, + "kmip_dbPointer_det_auto_id": { + "kms": "kmip", + "type": "dbPointer", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAM5B+fjbjYCZzCYUu4N/pJI3srCCXN+OCCHweeweqmpIEmB7yw87bQRIMGtCm6HuekcZ5J5q+nY5AQb0du/wh1YIoOrC3u4w7ZcLHkDmuAJPg=", + "subType": "06" + } + } + }, + "kmip_dbPointer_det_explicit_id": { + "kms": "kmip", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAM5B+fjbjYCZzCYUu4N/pJI3srCCXN+OCCHweeweqmpIEmB7yw87bQRIMGtCm6HuekcZ5J5q+nY5AQb0du/wh1YIoOrC3u4w7ZcLHkDmuAJPg=", + "subType": "06" + } + } + }, + "kmip_dbPointer_det_explicit_altname": { + "kms": "kmip", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAM5B+fjbjYCZzCYUu4N/pJI3srCCXN+OCCHweeweqmpIEmB7yw87bQRIMGtCm6HuekcZ5J5q+nY5AQb0du/wh1YIoOrC3u4w7ZcLHkDmuAJPg=", + "subType": "06" + } + } + }, + "kmip_javascript_rand_auto_id": { + "kms": "kmip", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAANuzlkWs/c8xArrAxPgYuCeShjj1zCfIMHOTPohspcyNofo9iY3P5MlhEOprZDiS8dBFg6EB7fZDzDdczx6VCN2A==", + "subType": "06" + } + } + }, + "kmip_javascript_rand_auto_altname": { + "kms": "kmip", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAANwJ72y7UqCBJh1NwVRiE3vU1ex7FMv/X5YWCMuO9MHPMo4g1V5eaO4KfOr+K8+9NtkflgMpeDkvwP92rfR5ud5Q==", + "subType": "06" + } + } + }, + "kmip_javascript_rand_explicit_id": { + "kms": "kmip", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAANj5q+888itRnLsw9PNGsBLhgqpvem5IJBOE2292r6zwjVueoEK/2I2PesRnn0esnkwdia1ADoMkcLUegwcFRkWQ==", + "subType": "06" + } + } + }, + "kmip_javascript_rand_explicit_altname": { + "kms": "kmip", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAANnvbnmApys7OIe8LGTsZKDG1F1G1SI/rfZVmF6q1fq5U7feYPp1ejb2t2S2+v7LfcOHytsQWGcYuWCDcl+vosvQ==", + "subType": "06" + } + } + }, + "kmip_javascript_det_auto_id": { + "kms": "kmip", + "type": "javascript", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAANOR9R/Da8j5iVxllLiGFlv4U/bVn/PyN9/5WeGJkGJeE/j/osKrKx6IL1igI0YVI+pKKzsINqJGIv+bJX0s7MNw==", + "subType": "06" + } + } + }, + "kmip_javascript_det_explicit_id": { + "kms": "kmip", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAANOR9R/Da8j5iVxllLiGFlv4U/bVn/PyN9/5WeGJkGJeE/j/osKrKx6IL1igI0YVI+pKKzsINqJGIv+bJX0s7MNw==", + "subType": "06" + } + } + }, + "kmip_javascript_det_explicit_altname": { + "kms": "kmip", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAANOR9R/Da8j5iVxllLiGFlv4U/bVn/PyN9/5WeGJkGJeE/j/osKrKx6IL1igI0YVI+pKKzsINqJGIv+bJX0s7MNw==", + "subType": "06" + } + } + }, + "kmip_symbol_rand_auto_id": { + "kms": "kmip", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAOe+vXpJSkmBM3WkxZrn4ea9/C6iNyMXWUzkQIzIYlnbkyu8od8nfOdhobUhoFxcKnvdaxN1s5NhJ1FA97RN/upGYN+AI/7cTCElmFSpdSvkI=", + "subType": "06" + } + } + }, + "kmip_symbol_rand_auto_altname": { + "kms": "kmip", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAOPpCgK6Hc/M2elOJkwIU9J7PZa+h1chody2yvfDu/UlB6T5sxnEZ6aEY/ISNLhJlhsRzuApSgFOmnrcG6Eg9VnSKin2yK0ll+VFxQEDHAcSA=", + "subType": "06" + } + } + }, + "kmip_symbol_rand_explicit_id": { + "kms": "kmip", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAOVoHX9GaOn71L5D9TpZmmxkx/asr0FHCLG5ZgLLA04yIhZHsDjt2DiVGGO/Mf4KwvoBn7Cf08qMhW7rQh2LgvvSLBO3zbw5l+MZ/bSn+Jylo=", + "subType": "06" + } + } + }, + "kmip_symbol_rand_explicit_altname": { + "kms": "kmip", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAOPobmcO/I4QObtCUEmGWpSCJ6tlYyhbO59q78LZBucSNl7DSkf/13tOJ9t+WKXACcMKVMmfPoFsgHbVj1nKWULBT07n1OWWDTZkuMD6C2+Fc=", + "subType": "06" + } + } + }, + "kmip_symbol_det_auto_id": { + "kms": "kmip", + "type": "symbol", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAOPpwX4mafoQJYHuzYfbKW1JunpjpB7Nd2slTC3n8Hsas9wQYf9VkModQhe5M4wZHOIXpehaODRcjKKfKRmpnNBOURSLm/ORJvy+UxtSLsnqo=", + "subType": "06" + } + } + }, + "kmip_symbol_det_explicit_id": { + "kms": "kmip", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAOPpwX4mafoQJYHuzYfbKW1JunpjpB7Nd2slTC3n8Hsas9wQYf9VkModQhe5M4wZHOIXpehaODRcjKKfKRmpnNBOURSLm/ORJvy+UxtSLsnqo=", + "subType": "06" + } + } + }, + "kmip_symbol_det_explicit_altname": { + "kms": "kmip", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAOPpwX4mafoQJYHuzYfbKW1JunpjpB7Nd2slTC3n8Hsas9wQYf9VkModQhe5M4wZHOIXpehaODRcjKKfKRmpnNBOURSLm/ORJvy+UxtSLsnqo=", + "subType": "06" + } + } + }, + "kmip_javascriptWithScope_rand_auto_id": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAPW2VMMm+EvsYpVtJQhsxgxgvV35kr9nxqKxP2qqIOAOQ58R/1oyYScFkNwB/tw0A1/zdvhoo+ERa7c0tjLIojFrosXhX2N/8Z4VnbZruz0Nk=", + "subType": "06" + } + } + }, + "kmip_javascriptWithScope_rand_auto_altname": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAPjPq9BQR4EwG/CD+RthOJY04m99LCl/shY6HnaU/QL627kN1dbBAG5vs+MXfa+glg8waVTNgB94vm3j72FMV1ZOKvbl4faWF1Rl2EOpOlR9U=", + "subType": "06" + } + } + }, + "kmip_javascriptWithScope_rand_explicit_id": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAPtqebrCAidKzBMvp3B5/vBeetqeCoMKS+vo+hLAYooXrnBunWxwRHpr45XYUvroG3aqOMkLtVZSgw8sO6Y/3z1viO2G0sGQW1ZMoW0/PX5Uw=", + "subType": "06" + } + } + }, + "kmip_javascriptWithScope_rand_explicit_altname": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAPtkJwXKlq8Fx1f1+9HFofM4uKi6lHQRFRyiOyUFJYxxZY1LR/2WXXTqWz3MWtrcJFCB+QSVOb1N/ieC7AZUboPgIuPJISM3Hu5VU2x/Isbdc=", + "subType": "06" + } + } + }, + "kmip_javascriptWithScope_det_explicit_id": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "kmip_javascriptWithScope_det_explicit_altname": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "kmip_int_rand_auto_id": { + "kms": "kmip", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAQ50kE7Tby9od2OsmIGZhp9k/mj4vy/YdnmF6YsSPxihbjV1vXGMraI/nGCr+0H1riwzq3m4sCT7aPw2VgiuwKMA==", + "subType": "06" + } + } + }, + "kmip_int_rand_auto_altname": { + "kms": "kmip", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAQkNL14OSMX/bJbsLtB/UumRoat6QOY7fvwZxRrkXTS3VJVHigthI1cUX7Is/uUsY8oHOfk/ZuHklQkifmfdcklQ==", + "subType": "06" + } + } + }, + "kmip_int_rand_explicit_id": { + "kms": "kmip", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAQtN2gNVU9Itoj+vgcK/4jEB5baSUH+Qz2WqTY7m0XaA3bPWGFCiWY4Sdw+qovednrSSSbC+azWi1QYclFRraldQ==", + "subType": "06" + } + } + }, + "kmip_int_rand_explicit_altname": { + "kms": "kmip", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAQk6uBqwXXFF9zEM4bc124goI3pBy2Jdi8Cd0ycKkjXrPG7GVCUm2UMbO+zEzYODeVo35N11g2yMXcv9RVgjWtNA==", + "subType": "06" + } + } + }, + "kmip_int_det_auto_id": { + "kms": "kmip", + "type": "int", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAQgrkPEf+RBZMn/J7HZObqEfus8icYls6ecaUrlabI6v1ALgxLuv23WSIfTr6mqpQCounqdA14DWS/Wl3kSkVC0w==", + "subType": "06" + } + } + }, + "kmip_int_det_explicit_id": { + "kms": "kmip", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAQgrkPEf+RBZMn/J7HZObqEfus8icYls6ecaUrlabI6v1ALgxLuv23WSIfTr6mqpQCounqdA14DWS/Wl3kSkVC0w==", + "subType": "06" + } + } + }, + "kmip_int_det_explicit_altname": { + "kms": "kmip", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAQgrkPEf+RBZMn/J7HZObqEfus8icYls6ecaUrlabI6v1ALgxLuv23WSIfTr6mqpQCounqdA14DWS/Wl3kSkVC0w==", + "subType": "06" + } + } + }, + "kmip_timestamp_rand_auto_id": { + "kms": "kmip", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAAR2Cu3o2e/u5o69MndeZPJU5ngVA1G2MNYn00t+up/GlmaUC1ni1CVl0ZR0EVZ0gCDUrfxwPISPib8y23tNjbsog==", + "subType": "06" + } + } + }, + "kmip_timestamp_rand_auto_altname": { + "kms": "kmip", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAARgi8stgSQwqnN4Ws2ZBILOREsjreZcS1MBerL7dbGLVfzW99tqECglhGokkrE0aY69L0xMgcAUIaFRN4GanQAPg==", + "subType": "06" + } + } + }, + "kmip_timestamp_rand_explicit_id": { + "kms": "kmip", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAARPxEEI8L5Q3Jybu88BLdf31T3uYEUbijgSlKlkTt141RYrlE8nxtiYU5/5H9GXBis0Qq1s2C+MauD2h/cNijTCA==", + "subType": "06" + } + } + }, + "kmip_timestamp_rand_explicit_altname": { + "kms": "kmip", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAARh/QaU1dnGbii4LtXCpT5o6vencc8E2fzarjJFbSEd0ixW/UV1ppZdvD729d0umkaIwIEVA4q+XVvHfl/ckKPFg==", + "subType": "06" + } + } + }, + "kmip_timestamp_det_auto_id": { + "kms": "kmip", + "type": "timestamp", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAARqdpLb72mmzb75QBrE+ATMfS5LLqzAD/1g5ScT8zfgh0IHsZZBWCJlSVRNC12Sgr3zdXHMtYp8C3OZT6/tPkQGg==", + "subType": "06" + } + } + }, + "kmip_timestamp_det_explicit_id": { + "kms": "kmip", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAARqdpLb72mmzb75QBrE+ATMfS5LLqzAD/1g5ScT8zfgh0IHsZZBWCJlSVRNC12Sgr3zdXHMtYp8C3OZT6/tPkQGg==", + "subType": "06" + } + } + }, + "kmip_timestamp_det_explicit_altname": { + "kms": "kmip", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAARqdpLb72mmzb75QBrE+ATMfS5LLqzAD/1g5ScT8zfgh0IHsZZBWCJlSVRNC12Sgr3zdXHMtYp8C3OZT6/tPkQGg==", + "subType": "06" + } + } + }, + "kmip_long_rand_auto_id": { + "kms": "kmip", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAASVv+ClXkh9spIaXWJYRV/o8UZjG+WWWrNpIjZ9LQn2bXakrKJ3REvdkrzGuxASmBhBYTplEyvxVCJwXuWRAGGYw==", + "subType": "06" + } + } + }, + "kmip_long_rand_auto_altname": { + "kms": "kmip", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAASeAz/dK+Gc4/jx3W07B2rNFvQ0LoyCllFRvRVGu1Xf1NByc4cRZLOMzlr99syz/fifF6WY30bOi5Pani9QtFuGg==", + "subType": "06" + } + } + }, + "kmip_long_rand_explicit_id": { + "kms": "kmip", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAASP1HD9uoDlwTldaznKxW71JUQcLsa4/cUWzeTnelQwdpohCbZsM8fBZBqgwwTWnjpYY/LBUipC6yhwLKfUXBoBQ==", + "subType": "06" + } + } + }, + "kmip_long_rand_explicit_altname": { + "kms": "kmip", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAASnGPH77bS/ETB1hn+VTvsBrxEvIHA6EAb8Z2SEz6BHt7SVeI+I7DLERvRVpV5kNJFcKgXDrvRmD+Et0rhSmk9sw==", + "subType": "06" + } + } + }, + "kmip_long_det_auto_id": { + "kms": "kmip", + "type": "long", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAS+zKmtijSTPOEVlpwmaeMIOuzVNuZpV4Jw9zP8Yqa1xYtlItXDozqdibacRaA74KU49KNySdR1T7fxwxa2OOTrQ==", + "subType": "06" + } + } + }, + "kmip_long_det_explicit_id": { + "kms": "kmip", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAS+zKmtijSTPOEVlpwmaeMIOuzVNuZpV4Jw9zP8Yqa1xYtlItXDozqdibacRaA74KU49KNySdR1T7fxwxa2OOTrQ==", + "subType": "06" + } + } + }, + "kmip_long_det_explicit_altname": { + "kms": "kmip", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASjCDwAAAAAAAAAAAAAAAAAS+zKmtijSTPOEVlpwmaeMIOuzVNuZpV4Jw9zP8Yqa1xYtlItXDozqdibacRaA74KU49KNySdR1T7fxwxa2OOTrQ==", + "subType": "06" + } + } + }, + "kmip_decimal_rand_auto_id": { + "kms": "kmip", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAATu/BbCc5Ti9SBlMR2B8zj3Q1yQ16Uob+10LWaT5QKS192IcnBGy4wmmNkIsTys060xUby9KKQF80dVPnjYfqJwEXCe/pVaPQZftE0DolKv78=", + "subType": "06" + } + } + }, + "kmip_decimal_rand_auto_altname": { + "kms": "kmip", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAATpq6/dtxq2ZUZHrK10aB0YjjPalEaXYcyAyRZjfXWAYCLZdT9sIybjX3Axjxisim+VSHx0QU7oXkKUfcbLgHyjUXj8g9059FHxKFkUsNv4Z8=", + "subType": "06" + } + } + }, + "kmip_decimal_rand_explicit_id": { + "kms": "kmip", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAATS++9KcfM7uiShZYxRpFPrBJquKv7dyvFRTjnxs6aaaPo0fiqpv6bco/cMLsldEVpWDEA/Tc2HtSXYPp4UJsMfASyBjoxCloL5SaRWyD9Ye8=", + "subType": "06" + } + } + }, + "kmip_decimal_rand_explicit_altname": { + "kms": "kmip", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AijCDwAAAAAAAAAAAAAAAAATREcETS5KoAGyj/P45owPrdFfy5ng8Z1ND+F+780lLddOyPeDnIsa7yg6uvhTZ65mHfGLvKcFocclYenq/AX1dY4xdjLRg/AfT088A27ORUA=", + "subType": "06" + } + } + }, + "kmip_decimal_det_explicit_id": { + "kms": "kmip", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "kmip_decimal_det_explicit_altname": { + "kms": "kmip", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "kmip_minKey_rand_explicit_id": { + "kms": "kmip", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "kmip_minKey_rand_explicit_altname": { + "kms": "kmip", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "kmip_minKey_det_explicit_id": { + "kms": "kmip", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "kmip_minKey_det_explicit_altname": { + "kms": "kmip", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "kmip_maxKey_rand_explicit_id": { + "kms": "kmip", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "kmip_maxKey_rand_explicit_altname": { + "kms": "kmip", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "kmip_maxKey_det_explicit_id": { + "kms": "kmip", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "kmip_maxKey_det_explicit_altname": { + "kms": "kmip", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } } -} +} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-key-aws.json b/tests/SpecTests/client-side-encryption/corpus/corpus-key-aws.json index 06f2f06dc..eca6cf912 100644 --- a/tests/SpecTests/client-side-encryption/corpus/corpus-key-aws.json +++ b/tests/SpecTests/client-side-encryption/corpus/corpus-key-aws.json @@ -1,29 +1,29 @@ { "status": { "$numberInt": "1" - }, + }, "_id": { "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", "subType": "04" } - }, + }, "masterKey": { - "region": "us-east-1", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", "provider": "aws" - }, + }, "updateDate": { "$date": { "$numberLong": "1557827033449" } - }, + }, "keyMaterial": { "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", "subType": "00" } - }, + }, "creationDate": { "$date": { "$numberLong": "1557827033449" diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-key-azure.json b/tests/SpecTests/client-side-encryption/corpus/corpus-key-azure.json new file mode 100644 index 000000000..31a564edb --- /dev/null +++ b/tests/SpecTests/client-side-encryption/corpus/corpus-key-azure.json @@ -0,0 +1,33 @@ +{ + "_id": { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "n+HWZ0ZSVOYA3cvQgP7inN4JSXfOH85IngmeQxRpQHjCCcqT3IFqEWNlrsVHiz3AELimHhX4HKqOLWMUeSIT6emUDDoQX9BAv8DR1+E1w4nGs/NyEneac78EYFkK3JysrFDOgl2ypCCTKAypkn9CkAx1if4cfgQE93LW4kczcyHdGiH36CIxrCDGv1UzAvERN5Qa47DVwsM6a+hWsF2AAAJVnF0wYLLJU07TuRHdMrrphPWXZsFgyV+lRqJ7DDpReKNO8nMPLV/mHqHBHGPGQiRdb9NoJo8CvokGz4+KE8oLwzKf6V24dtwZmRkrsDV4iOhvROAzz+Euo1ypSkL3mw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1601573901680" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1601573901680" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyAltNames": ["azure"] +} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-key-gcp.json b/tests/SpecTests/client-side-encryption/corpus/corpus-key-gcp.json new file mode 100644 index 000000000..79d6999b0 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/corpus/corpus-key-gcp.json @@ -0,0 +1,35 @@ +{ + "_id": { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "CiQAIgLj0WyktnB4dfYHo5SLZ41K4ASQrjJUaSzl5vvVH0G12G0SiQEAjlV8XPlbnHDEDFbdTO4QIe8ER2/172U1ouLazG0ysDtFFIlSvWX5ZnZUrRMmp/R2aJkzLXEt/zf8Mn4Lfm+itnjgo5R9K4pmPNvvPKNZX5C16lrPT+aA+rd+zXFSmlMg3i5jnxvTdLHhg3G7Q/Uv1ZIJskKt95bzLoe0tUVzRWMYXLIEcohnQg==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1601574333107" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1601574333107" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyAltNames": ["gcp"] +} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-key-kmip.json b/tests/SpecTests/client-side-encryption/corpus/corpus-key-kmip.json new file mode 100644 index 000000000..7c7069700 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/corpus/corpus-key-kmip.json @@ -0,0 +1,32 @@ +{ + "_id": { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "eUYDyB0HuWb+lQgUwO+6qJQyTTDTY2gp9FbemL7ZFo0pvr0x6rm6Ff9OVUTGH6HyMKipaeHdiIJU1dzsLwvqKvi7Beh+U4iaIWX/K0oEg1GOsJc0+Z/in8gNHbGUYLmycHViM3LES3kdt7FdFSUl5rEBHrM71yoNEXImz17QJWMGOuT4x6yoi2pvnaRJwfrI4DjpmnnTrDMac92jgZehbg==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "kmip", + "keyId": "1" + }, + "keyAltNames": ["kmip"] +} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-key-local.json b/tests/SpecTests/client-side-encryption/corpus/corpus-key-local.json index 5da84ab6b..b3fe0723b 100644 --- a/tests/SpecTests/client-side-encryption/corpus/corpus-key-local.json +++ b/tests/SpecTests/client-side-encryption/corpus/corpus-key-local.json @@ -1,27 +1,27 @@ { "status": { "$numberInt": "1" - }, + }, "_id": { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } - }, + }, "masterKey": { "provider": "local" - }, + }, "updateDate": { "$date": { "$numberLong": "1557827033449" } - }, + }, "keyMaterial": { "$binary": { - "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", + "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", "subType": "00" } - }, + }, "creationDate": { "$date": { "$numberLong": "1557827033449" diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-schema.json b/tests/SpecTests/client-side-encryption/corpus/corpus-schema.json index e4838d8aa..e74bc914f 100644 --- a/tests/SpecTests/client-side-encryption/corpus/corpus-schema.json +++ b/tests/SpecTests/client-side-encryption/corpus/corpus-schema.json @@ -34,11 +34,19 @@ }, "aws_double_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_double_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_string_rand_auto_id": { "bsonType": "object", @@ -73,11 +81,19 @@ }, "aws_string_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_string_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_string_det_auto_id": { "bsonType": "object", @@ -100,11 +116,19 @@ }, "aws_string_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_string_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_object_rand_auto_id": { "bsonType": "object", @@ -139,11 +163,19 @@ }, "aws_object_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_object_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_array_rand_auto_id": { "bsonType": "object", @@ -178,11 +210,19 @@ }, "aws_array_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_array_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_binData=00_rand_auto_id": { "bsonType": "object", @@ -217,11 +257,19 @@ }, "aws_binData=00_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_binData=00_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_binData=00_det_auto_id": { "bsonType": "object", @@ -244,11 +292,19 @@ }, "aws_binData=00_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_binData=00_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_binData=04_rand_auto_id": { "bsonType": "object", @@ -283,11 +339,19 @@ }, "aws_binData=04_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_binData=04_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_binData=04_det_auto_id": { "bsonType": "object", @@ -310,11 +374,19 @@ }, "aws_binData=04_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_binData=04_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_objectId_rand_auto_id": { "bsonType": "object", @@ -349,11 +421,19 @@ }, "aws_objectId_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_objectId_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_objectId_det_auto_id": { "bsonType": "object", @@ -376,11 +456,19 @@ }, "aws_objectId_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_objectId_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_bool_rand_auto_id": { "bsonType": "object", @@ -415,11 +503,19 @@ }, "aws_bool_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_bool_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_date_rand_auto_id": { "bsonType": "object", @@ -454,11 +550,19 @@ }, "aws_date_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_date_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_date_det_auto_id": { "bsonType": "object", @@ -481,11 +585,19 @@ }, "aws_date_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_date_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_regex_rand_auto_id": { "bsonType": "object", @@ -520,11 +632,19 @@ }, "aws_regex_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_regex_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_regex_det_auto_id": { "bsonType": "object", @@ -547,11 +667,19 @@ }, "aws_regex_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_regex_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_dbPointer_rand_auto_id": { "bsonType": "object", @@ -586,11 +714,19 @@ }, "aws_dbPointer_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_dbPointer_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_dbPointer_det_auto_id": { "bsonType": "object", @@ -613,11 +749,19 @@ }, "aws_dbPointer_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_dbPointer_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_javascript_rand_auto_id": { "bsonType": "object", @@ -652,11 +796,19 @@ }, "aws_javascript_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_javascript_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_javascript_det_auto_id": { "bsonType": "object", @@ -679,11 +831,19 @@ }, "aws_javascript_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_javascript_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_symbol_rand_auto_id": { "bsonType": "object", @@ -718,11 +878,19 @@ }, "aws_symbol_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_symbol_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_symbol_det_auto_id": { "bsonType": "object", @@ -745,11 +913,19 @@ }, "aws_symbol_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_symbol_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_javascriptWithScope_rand_auto_id": { "bsonType": "object", @@ -784,11 +960,19 @@ }, "aws_javascriptWithScope_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_javascriptWithScope_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_int_rand_auto_id": { "bsonType": "object", @@ -823,11 +1007,19 @@ }, "aws_int_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_int_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_int_det_auto_id": { "bsonType": "object", @@ -850,11 +1042,19 @@ }, "aws_int_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_int_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_timestamp_rand_auto_id": { "bsonType": "object", @@ -889,11 +1089,19 @@ }, "aws_timestamp_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_timestamp_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_timestamp_det_auto_id": { "bsonType": "object", @@ -916,11 +1124,19 @@ }, "aws_timestamp_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_timestamp_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_long_rand_auto_id": { "bsonType": "object", @@ -955,11 +1171,19 @@ }, "aws_long_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_long_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_long_det_auto_id": { "bsonType": "object", @@ -982,11 +1206,19 @@ }, "aws_long_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_long_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_decimal_rand_auto_id": { "bsonType": "object", @@ -1021,11 +1253,19 @@ }, "aws_decimal_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "aws_decimal_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_double_rand_auto_id": { "bsonType": "object", @@ -1060,11 +1300,19 @@ }, "local_double_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_double_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_string_rand_auto_id": { "bsonType": "object", @@ -1099,11 +1347,19 @@ }, "local_string_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_string_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_string_det_auto_id": { "bsonType": "object", @@ -1126,11 +1382,19 @@ }, "local_string_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_string_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_object_rand_auto_id": { "bsonType": "object", @@ -1165,11 +1429,19 @@ }, "local_object_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_object_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_array_rand_auto_id": { "bsonType": "object", @@ -1204,11 +1476,19 @@ }, "local_array_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_array_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_binData=00_rand_auto_id": { "bsonType": "object", @@ -1243,11 +1523,19 @@ }, "local_binData=00_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_binData=00_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_binData=00_det_auto_id": { "bsonType": "object", @@ -1270,11 +1558,19 @@ }, "local_binData=00_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_binData=00_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_binData=04_rand_auto_id": { "bsonType": "object", @@ -1309,11 +1605,19 @@ }, "local_binData=04_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_binData=04_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_binData=04_det_auto_id": { "bsonType": "object", @@ -1336,11 +1640,19 @@ }, "local_binData=04_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_binData=04_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_objectId_rand_auto_id": { "bsonType": "object", @@ -1375,11 +1687,19 @@ }, "local_objectId_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_objectId_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_objectId_det_auto_id": { "bsonType": "object", @@ -1402,11 +1722,19 @@ }, "local_objectId_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_objectId_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_bool_rand_auto_id": { "bsonType": "object", @@ -1441,11 +1769,19 @@ }, "local_bool_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_bool_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_date_rand_auto_id": { "bsonType": "object", @@ -1480,11 +1816,19 @@ }, "local_date_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_date_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_date_det_auto_id": { "bsonType": "object", @@ -1507,11 +1851,19 @@ }, "local_date_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_date_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_regex_rand_auto_id": { "bsonType": "object", @@ -1546,11 +1898,19 @@ }, "local_regex_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_regex_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_regex_det_auto_id": { "bsonType": "object", @@ -1573,11 +1933,19 @@ }, "local_regex_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_regex_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_dbPointer_rand_auto_id": { "bsonType": "object", @@ -1612,11 +1980,19 @@ }, "local_dbPointer_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_dbPointer_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_dbPointer_det_auto_id": { "bsonType": "object", @@ -1639,11 +2015,19 @@ }, "local_dbPointer_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_dbPointer_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_javascript_rand_auto_id": { "bsonType": "object", @@ -1678,11 +2062,19 @@ }, "local_javascript_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_javascript_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_javascript_det_auto_id": { "bsonType": "object", @@ -1705,11 +2097,19 @@ }, "local_javascript_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_javascript_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_symbol_rand_auto_id": { "bsonType": "object", @@ -1744,11 +2144,19 @@ }, "local_symbol_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_symbol_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_symbol_det_auto_id": { "bsonType": "object", @@ -1771,11 +2179,19 @@ }, "local_symbol_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_symbol_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_javascriptWithScope_rand_auto_id": { "bsonType": "object", @@ -1810,11 +2226,19 @@ }, "local_javascriptWithScope_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_javascriptWithScope_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_int_rand_auto_id": { "bsonType": "object", @@ -1849,11 +2273,19 @@ }, "local_int_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_int_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_int_det_auto_id": { "bsonType": "object", @@ -1876,11 +2308,19 @@ }, "local_int_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_int_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_timestamp_rand_auto_id": { "bsonType": "object", @@ -1915,11 +2355,19 @@ }, "local_timestamp_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_timestamp_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_timestamp_det_auto_id": { "bsonType": "object", @@ -1942,11 +2390,19 @@ }, "local_timestamp_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_timestamp_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_long_rand_auto_id": { "bsonType": "object", @@ -1981,11 +2437,19 @@ }, "local_long_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_long_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_long_det_auto_id": { "bsonType": "object", @@ -2008,11 +2472,19 @@ }, "local_long_det_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_long_det_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_decimal_rand_auto_id": { "bsonType": "object", @@ -2047,11 +2519,3817 @@ }, "local_decimal_rand_explicit_id": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } }, "local_decimal_rand_explicit_altname": { "bsonType": "object", - "properties": { "value": { "bsonType": "binData" } } + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_double_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "double" + } + } + } + }, + "azure_double_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "double" + } + } + } + }, + "azure_double_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_double_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_string_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "string" + } + } + } + }, + "azure_string_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "string" + } + } + } + }, + "azure_string_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_string_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_string_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "string" + } + } + } + }, + "azure_string_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_string_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_object_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "object" + } + } + } + }, + "azure_object_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "object" + } + } + } + }, + "azure_object_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_object_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_array_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "array" + } + } + } + }, + "azure_array_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "array" + } + } + } + }, + "azure_array_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_array_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_binData=00_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "azure_binData=00_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "azure_binData=00_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_binData=00_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_binData=00_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "binData" + } + } + } + }, + "azure_binData=00_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_binData=00_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_binData=04_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "azure_binData=04_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "azure_binData=04_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_binData=04_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_binData=04_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "binData" + } + } + } + }, + "azure_binData=04_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_binData=04_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_objectId_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "objectId" + } + } + } + }, + "azure_objectId_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "objectId" + } + } + } + }, + "azure_objectId_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_objectId_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_objectId_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "objectId" + } + } + } + }, + "azure_objectId_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_objectId_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_bool_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "bool" + } + } + } + }, + "azure_bool_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "bool" + } + } + } + }, + "azure_bool_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_bool_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_date_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "date" + } + } + } + }, + "azure_date_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "date" + } + } + } + }, + "azure_date_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_date_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_date_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "date" + } + } + } + }, + "azure_date_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_date_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_regex_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "regex" + } + } + } + }, + "azure_regex_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "regex" + } + } + } + }, + "azure_regex_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_regex_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_regex_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "regex" + } + } + } + }, + "azure_regex_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_regex_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_dbPointer_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "dbPointer" + } + } + } + }, + "azure_dbPointer_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "dbPointer" + } + } + } + }, + "azure_dbPointer_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_dbPointer_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_dbPointer_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "dbPointer" + } + } + } + }, + "azure_dbPointer_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_dbPointer_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_javascript_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascript" + } + } + } + }, + "azure_javascript_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascript" + } + } + } + }, + "azure_javascript_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_javascript_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_javascript_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "javascript" + } + } + } + }, + "azure_javascript_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_javascript_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_symbol_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "symbol" + } + } + } + }, + "azure_symbol_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "symbol" + } + } + } + }, + "azure_symbol_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_symbol_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_symbol_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "symbol" + } + } + } + }, + "azure_symbol_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_symbol_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_javascriptWithScope_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascriptWithScope" + } + } + } + }, + "azure_javascriptWithScope_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascriptWithScope" + } + } + } + }, + "azure_javascriptWithScope_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_javascriptWithScope_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_int_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "int" + } + } + } + }, + "azure_int_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "int" + } + } + } + }, + "azure_int_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_int_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_int_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "int" + } + } + } + }, + "azure_int_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_int_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_timestamp_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "timestamp" + } + } + } + }, + "azure_timestamp_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "timestamp" + } + } + } + }, + "azure_timestamp_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_timestamp_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_timestamp_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "timestamp" + } + } + } + }, + "azure_timestamp_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_timestamp_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_long_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "long" + } + } + } + }, + "azure_long_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "long" + } + } + } + }, + "azure_long_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_long_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_long_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "long" + } + } + } + }, + "azure_long_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_long_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_decimal_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZUREAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "decimal" + } + } + } + }, + "azure_decimal_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_azure", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "decimal" + } + } + } + }, + "azure_decimal_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "azure_decimal_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_double_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "double" + } + } + } + }, + "gcp_double_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "double" + } + } + } + }, + "gcp_double_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_double_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_string_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "string" + } + } + } + }, + "gcp_string_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "string" + } + } + } + }, + "gcp_string_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_string_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_string_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "string" + } + } + } + }, + "gcp_string_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_string_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_object_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "object" + } + } + } + }, + "gcp_object_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "object" + } + } + } + }, + "gcp_object_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_object_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_array_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "array" + } + } + } + }, + "gcp_array_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "array" + } + } + } + }, + "gcp_array_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_array_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_binData=00_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "gcp_binData=00_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "gcp_binData=00_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_binData=00_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_binData=00_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "binData" + } + } + } + }, + "gcp_binData=00_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_binData=00_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_binData=04_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "gcp_binData=04_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "gcp_binData=04_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_binData=04_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_binData=04_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "binData" + } + } + } + }, + "gcp_binData=04_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_binData=04_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_objectId_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "objectId" + } + } + } + }, + "gcp_objectId_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "objectId" + } + } + } + }, + "gcp_objectId_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_objectId_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_objectId_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "objectId" + } + } + } + }, + "gcp_objectId_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_objectId_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_bool_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "bool" + } + } + } + }, + "gcp_bool_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "bool" + } + } + } + }, + "gcp_bool_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_bool_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_date_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "date" + } + } + } + }, + "gcp_date_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "date" + } + } + } + }, + "gcp_date_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_date_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_date_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "date" + } + } + } + }, + "gcp_date_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_date_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_regex_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "regex" + } + } + } + }, + "gcp_regex_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "regex" + } + } + } + }, + "gcp_regex_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_regex_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_regex_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "regex" + } + } + } + }, + "gcp_regex_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_regex_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_dbPointer_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "dbPointer" + } + } + } + }, + "gcp_dbPointer_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "dbPointer" + } + } + } + }, + "gcp_dbPointer_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_dbPointer_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_dbPointer_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "dbPointer" + } + } + } + }, + "gcp_dbPointer_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_dbPointer_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_javascript_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascript" + } + } + } + }, + "gcp_javascript_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascript" + } + } + } + }, + "gcp_javascript_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_javascript_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_javascript_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "javascript" + } + } + } + }, + "gcp_javascript_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_javascript_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_symbol_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "symbol" + } + } + } + }, + "gcp_symbol_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "symbol" + } + } + } + }, + "gcp_symbol_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_symbol_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_symbol_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "symbol" + } + } + } + }, + "gcp_symbol_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_symbol_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_javascriptWithScope_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascriptWithScope" + } + } + } + }, + "gcp_javascriptWithScope_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascriptWithScope" + } + } + } + }, + "gcp_javascriptWithScope_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_javascriptWithScope_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_int_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "int" + } + } + } + }, + "gcp_int_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "int" + } + } + } + }, + "gcp_int_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_int_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_int_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "int" + } + } + } + }, + "gcp_int_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_int_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_timestamp_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "timestamp" + } + } + } + }, + "gcp_timestamp_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "timestamp" + } + } + } + }, + "gcp_timestamp_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_timestamp_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_timestamp_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "timestamp" + } + } + } + }, + "gcp_timestamp_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_timestamp_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_long_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "long" + } + } + } + }, + "gcp_long_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "long" + } + } + } + }, + "gcp_long_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_long_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_long_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "long" + } + } + } + }, + "gcp_long_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_long_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_decimal_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCPAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "decimal" + } + } + } + }, + "gcp_decimal_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_gcp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "decimal" + } + } + } + }, + "gcp_decimal_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "gcp_decimal_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_double_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "double" + } + } + } + }, + "kmip_double_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "double" + } + } + } + }, + "kmip_double_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_double_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_string_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "string" + } + } + } + }, + "kmip_string_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "string" + } + } + } + }, + "kmip_string_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_string_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_string_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "string" + } + } + } + }, + "kmip_string_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_string_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_object_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "object" + } + } + } + }, + "kmip_object_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "object" + } + } + } + }, + "kmip_object_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_object_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_array_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "array" + } + } + } + }, + "kmip_array_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "array" + } + } + } + }, + "kmip_array_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_array_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_binData=00_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "kmip_binData=00_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "kmip_binData=00_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_binData=00_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_binData=00_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "binData" + } + } + } + }, + "kmip_binData=00_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_binData=00_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_binData=04_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "kmip_binData=04_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "kmip_binData=04_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_binData=04_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_binData=04_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "binData" + } + } + } + }, + "kmip_binData=04_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_binData=04_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_objectId_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "objectId" + } + } + } + }, + "kmip_objectId_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "objectId" + } + } + } + }, + "kmip_objectId_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_objectId_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_objectId_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "objectId" + } + } + } + }, + "kmip_objectId_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_objectId_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_bool_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "bool" + } + } + } + }, + "kmip_bool_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "bool" + } + } + } + }, + "kmip_bool_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_bool_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_date_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "date" + } + } + } + }, + "kmip_date_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "date" + } + } + } + }, + "kmip_date_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_date_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_date_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "date" + } + } + } + }, + "kmip_date_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_date_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_regex_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "regex" + } + } + } + }, + "kmip_regex_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "regex" + } + } + } + }, + "kmip_regex_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_regex_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_regex_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "regex" + } + } + } + }, + "kmip_regex_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_regex_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_dbPointer_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "dbPointer" + } + } + } + }, + "kmip_dbPointer_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "dbPointer" + } + } + } + }, + "kmip_dbPointer_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_dbPointer_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_dbPointer_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "dbPointer" + } + } + } + }, + "kmip_dbPointer_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_dbPointer_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_javascript_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascript" + } + } + } + }, + "kmip_javascript_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascript" + } + } + } + }, + "kmip_javascript_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_javascript_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_javascript_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "javascript" + } + } + } + }, + "kmip_javascript_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_javascript_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_symbol_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "symbol" + } + } + } + }, + "kmip_symbol_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "symbol" + } + } + } + }, + "kmip_symbol_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_symbol_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_symbol_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "symbol" + } + } + } + }, + "kmip_symbol_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_symbol_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_javascriptWithScope_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascriptWithScope" + } + } + } + }, + "kmip_javascriptWithScope_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascriptWithScope" + } + } + } + }, + "kmip_javascriptWithScope_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_javascriptWithScope_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_int_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "int" + } + } + } + }, + "kmip_int_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "int" + } + } + } + }, + "kmip_int_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_int_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_int_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "int" + } + } + } + }, + "kmip_int_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_int_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_timestamp_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "timestamp" + } + } + } + }, + "kmip_timestamp_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "timestamp" + } + } + } + }, + "kmip_timestamp_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_timestamp_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_timestamp_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "timestamp" + } + } + } + }, + "kmip_timestamp_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_timestamp_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_long_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "long" + } + } + } + }, + "kmip_long_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "long" + } + } + } + }, + "kmip_long_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_long_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_long_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "long" + } + } + } + }, + "kmip_long_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_long_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_decimal_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "KMIPAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "decimal" + } + } + } + }, + "kmip_decimal_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_kmip", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "decimal" + } + } + } + }, + "kmip_decimal_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "kmip_decimal_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } } } -} +} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus.json b/tests/SpecTests/client-side-encryption/corpus/corpus.json index cbf7a091a..559711b34 100644 --- a/tests/SpecTests/client-side-encryption/corpus/corpus.json +++ b/tests/SpecTests/client-side-encryption/corpus/corpus.json @@ -2,6 +2,9 @@ "_id": "client_side_encryption_corpus", "altname_aws": "aws", "altname_local": "local", + "altname_azure": "azure", + "altname_gcp": "gcp", + "altname_kmip": "kmip", "aws_double_rand_auto_id": { "kms": "aws", "type": "double", @@ -9,7 +12,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberDouble": "1.234" } + "value": { + "$numberDouble": "1.234" + } }, "aws_double_rand_auto_altname": { "kms": "aws", @@ -18,7 +23,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$numberDouble": "1.234" } + "value": { + "$numberDouble": "1.234" + } }, "aws_double_rand_explicit_id": { "kms": "aws", @@ -27,7 +34,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberDouble": "1.234" } + "value": { + "$numberDouble": "1.234" + } }, "aws_double_rand_explicit_altname": { "kms": "aws", @@ -36,7 +45,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberDouble": "1.234" } + "value": { + "$numberDouble": "1.234" + } }, "aws_double_det_explicit_id": { "kms": "aws", @@ -45,7 +56,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$numberDouble": "1.234" } + "value": { + "$numberDouble": "1.234" + } }, "aws_double_det_explicit_altname": { "kms": "aws", @@ -54,7 +67,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$numberDouble": "1.234" } + "value": { + "$numberDouble": "1.234" + } }, "aws_string_rand_auto_id": { "kms": "aws", @@ -126,7 +141,11 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "aws_object_rand_auto_altname": { "kms": "aws", @@ -135,7 +154,11 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "aws_object_rand_explicit_id": { "kms": "aws", @@ -144,7 +167,11 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "aws_object_rand_explicit_altname": { "kms": "aws", @@ -153,7 +180,11 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "aws_object_det_explicit_id": { "kms": "aws", @@ -162,7 +193,11 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "aws_object_det_explicit_altname": { "kms": "aws", @@ -171,7 +206,11 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "aws_array_rand_auto_id": { "kms": "aws", @@ -181,9 +220,15 @@ "identifier": "id", "allowed": true, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "aws_array_rand_auto_altname": { @@ -194,9 +239,15 @@ "identifier": "altname", "allowed": true, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "aws_array_rand_explicit_id": { @@ -207,9 +258,15 @@ "identifier": "id", "allowed": true, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "aws_array_rand_explicit_altname": { @@ -220,9 +277,15 @@ "identifier": "altname", "allowed": true, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "aws_array_det_explicit_id": { @@ -233,9 +296,15 @@ "identifier": "id", "allowed": false, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "aws_array_det_explicit_altname": { @@ -246,9 +315,15 @@ "identifier": "altname", "allowed": false, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "aws_binData=00_rand_auto_id": { @@ -258,7 +333,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "aws_binData=00_rand_auto_altname": { "kms": "aws", @@ -267,7 +347,12 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "aws_binData=00_rand_explicit_id": { "kms": "aws", @@ -276,7 +361,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "aws_binData=00_rand_explicit_altname": { "kms": "aws", @@ -285,7 +375,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "aws_binData=00_det_auto_id": { "kms": "aws", @@ -294,7 +389,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "aws_binData=00_det_explicit_id": { "kms": "aws", @@ -303,7 +403,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "aws_binData=00_det_explicit_altname": { "kms": "aws", @@ -312,7 +417,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "aws_binData=04_rand_auto_id": { "kms": "aws", @@ -322,7 +432,10 @@ "identifier": "id", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "aws_binData=04_rand_auto_altname": { @@ -333,7 +446,10 @@ "identifier": "altname", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "aws_binData=04_rand_explicit_id": { @@ -344,7 +460,10 @@ "identifier": "id", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "aws_binData=04_rand_explicit_altname": { @@ -355,7 +474,10 @@ "identifier": "altname", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "aws_binData=04_det_auto_id": { @@ -366,7 +488,10 @@ "identifier": "id", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "aws_binData=04_det_explicit_id": { @@ -377,7 +502,10 @@ "identifier": "id", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "aws_binData=04_det_explicit_altname": { @@ -388,7 +516,10 @@ "identifier": "altname", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "aws_undefined_rand_explicit_id": { @@ -398,7 +529,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$undefined": true } + "value": { + "$undefined": true + } }, "aws_undefined_rand_explicit_altname": { "kms": "aws", @@ -407,7 +540,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$undefined": true } + "value": { + "$undefined": true + } }, "aws_undefined_det_explicit_id": { "kms": "aws", @@ -416,7 +551,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$undefined": true } + "value": { + "$undefined": true + } }, "aws_undefined_det_explicit_altname": { "kms": "aws", @@ -425,7 +562,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$undefined": true } + "value": { + "$undefined": true + } }, "aws_objectId_rand_auto_id": { "kms": "aws", @@ -434,7 +573,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "aws_objectId_rand_auto_altname": { "kms": "aws", @@ -443,7 +584,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "aws_objectId_rand_explicit_id": { "kms": "aws", @@ -452,7 +595,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "aws_objectId_rand_explicit_altname": { "kms": "aws", @@ -461,7 +606,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "aws_objectId_det_auto_id": { "kms": "aws", @@ -470,7 +617,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "aws_objectId_det_explicit_id": { "kms": "aws", @@ -479,7 +628,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "aws_objectId_det_explicit_altname": { "kms": "aws", @@ -488,7 +639,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "aws_bool_rand_auto_id": { "kms": "aws", @@ -551,7 +704,11 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "aws_date_rand_auto_altname": { "kms": "aws", @@ -560,7 +717,11 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "aws_date_rand_explicit_id": { "kms": "aws", @@ -569,7 +730,11 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "aws_date_rand_explicit_altname": { "kms": "aws", @@ -578,7 +743,11 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "aws_date_det_auto_id": { "kms": "aws", @@ -587,7 +756,11 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "aws_date_det_explicit_id": { "kms": "aws", @@ -596,7 +769,11 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "aws_date_det_explicit_altname": { "kms": "aws", @@ -605,7 +782,11 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "aws_null_rand_explicit_id": { "kms": "aws", @@ -650,7 +831,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "aws_regex_rand_auto_altname": { "kms": "aws", @@ -659,7 +845,12 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "aws_regex_rand_explicit_id": { "kms": "aws", @@ -668,7 +859,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "aws_regex_rand_explicit_altname": { "kms": "aws", @@ -677,7 +873,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "aws_regex_det_auto_id": { "kms": "aws", @@ -686,7 +887,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "aws_regex_det_explicit_id": { "kms": "aws", @@ -695,7 +901,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "aws_regex_det_explicit_altname": { "kms": "aws", @@ -704,7 +915,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "aws_dbPointer_rand_auto_id": { "kms": "aws", @@ -716,7 +932,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -730,7 +948,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -744,7 +964,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -758,7 +980,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -772,7 +996,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -786,7 +1012,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -800,7 +1028,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -811,7 +1041,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "aws_javascript_rand_auto_altname": { "kms": "aws", @@ -820,7 +1052,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "aws_javascript_rand_explicit_id": { "kms": "aws", @@ -829,7 +1063,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "aws_javascript_rand_explicit_altname": { "kms": "aws", @@ -838,7 +1074,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "aws_javascript_det_auto_id": { "kms": "aws", @@ -847,7 +1085,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "aws_javascript_det_explicit_id": { "kms": "aws", @@ -856,7 +1096,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "aws_javascript_det_explicit_altname": { "kms": "aws", @@ -865,7 +1107,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "aws_symbol_rand_auto_id": { "kms": "aws", @@ -874,7 +1118,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "aws_symbol_rand_auto_altname": { "kms": "aws", @@ -883,7 +1129,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "aws_symbol_rand_explicit_id": { "kms": "aws", @@ -892,7 +1140,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "aws_symbol_rand_explicit_altname": { "kms": "aws", @@ -901,7 +1151,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "aws_symbol_det_auto_id": { "kms": "aws", @@ -910,7 +1162,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "aws_symbol_det_explicit_id": { "kms": "aws", @@ -919,7 +1173,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "aws_symbol_det_explicit_altname": { "kms": "aws", @@ -928,7 +1184,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "aws_javascriptWithScope_rand_auto_id": { "kms": "aws", @@ -937,7 +1195,10 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "aws_javascriptWithScope_rand_auto_altname": { "kms": "aws", @@ -946,7 +1207,10 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "aws_javascriptWithScope_rand_explicit_id": { "kms": "aws", @@ -955,7 +1219,10 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "aws_javascriptWithScope_rand_explicit_altname": { "kms": "aws", @@ -964,7 +1231,10 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "aws_javascriptWithScope_det_explicit_id": { "kms": "aws", @@ -973,7 +1243,10 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "aws_javascriptWithScope_det_explicit_altname": { "kms": "aws", @@ -982,7 +1255,10 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "aws_int_rand_auto_id": { "kms": "aws", @@ -991,7 +1267,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "aws_int_rand_auto_altname": { "kms": "aws", @@ -1000,7 +1278,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "aws_int_rand_explicit_id": { "kms": "aws", @@ -1009,7 +1289,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "aws_int_rand_explicit_altname": { "kms": "aws", @@ -1018,7 +1300,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "aws_int_det_auto_id": { "kms": "aws", @@ -1027,7 +1311,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "aws_int_det_explicit_id": { "kms": "aws", @@ -1036,7 +1322,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "aws_int_det_explicit_altname": { "kms": "aws", @@ -1045,7 +1333,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "aws_timestamp_rand_auto_id": { "kms": "aws", @@ -1054,7 +1344,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "aws_timestamp_rand_auto_altname": { "kms": "aws", @@ -1063,7 +1358,12 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "aws_timestamp_rand_explicit_id": { "kms": "aws", @@ -1072,7 +1372,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "aws_timestamp_rand_explicit_altname": { "kms": "aws", @@ -1081,7 +1386,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "aws_timestamp_det_auto_id": { "kms": "aws", @@ -1090,7 +1400,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "aws_timestamp_det_explicit_id": { "kms": "aws", @@ -1099,7 +1414,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "aws_timestamp_det_explicit_altname": { "kms": "aws", @@ -1108,7 +1428,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "aws_long_rand_auto_id": { "kms": "aws", @@ -1117,7 +1442,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "aws_long_rand_auto_altname": { "kms": "aws", @@ -1126,7 +1453,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "aws_long_rand_explicit_id": { "kms": "aws", @@ -1135,7 +1464,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "aws_long_rand_explicit_altname": { "kms": "aws", @@ -1144,7 +1475,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "aws_long_det_auto_id": { "kms": "aws", @@ -1153,7 +1486,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "aws_long_det_explicit_id": { "kms": "aws", @@ -1162,7 +1497,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "aws_long_det_explicit_altname": { "kms": "aws", @@ -1171,7 +1508,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "aws_decimal_rand_auto_id": { "kms": "aws", @@ -1180,7 +1519,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "aws_decimal_rand_auto_altname": { "kms": "aws", @@ -1189,7 +1530,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "aws_decimal_rand_explicit_id": { "kms": "aws", @@ -1198,7 +1541,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "aws_decimal_rand_explicit_altname": { "kms": "aws", @@ -1207,7 +1552,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "aws_decimal_det_explicit_id": { "kms": "aws", @@ -1216,7 +1563,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "aws_decimal_det_explicit_altname": { "kms": "aws", @@ -1225,7 +1574,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "aws_minKey_rand_explicit_id": { "kms": "aws", @@ -1234,7 +1585,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$minKey": 1 } + "value": { + "$minKey": 1 + } }, "aws_minKey_rand_explicit_altname": { "kms": "aws", @@ -1243,7 +1596,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$minKey": 1 } + "value": { + "$minKey": 1 + } }, "aws_minKey_det_explicit_id": { "kms": "aws", @@ -1252,7 +1607,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$minKey": 1 } + "value": { + "$minKey": 1 + } }, "aws_minKey_det_explicit_altname": { "kms": "aws", @@ -1261,7 +1618,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$minKey": 1 } + "value": { + "$minKey": 1 + } }, "aws_maxKey_rand_explicit_id": { "kms": "aws", @@ -1270,7 +1629,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$maxKey": 1 } + "value": { + "$maxKey": 1 + } }, "aws_maxKey_rand_explicit_altname": { "kms": "aws", @@ -1279,7 +1640,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$maxKey": 1 } + "value": { + "$maxKey": 1 + } }, "aws_maxKey_det_explicit_id": { "kms": "aws", @@ -1288,7 +1651,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$maxKey": 1 } + "value": { + "$maxKey": 1 + } }, "aws_maxKey_det_explicit_altname": { "kms": "aws", @@ -1297,7 +1662,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$maxKey": 1 } + "value": { + "$maxKey": 1 + } }, "local_double_rand_auto_id": { "kms": "local", @@ -1306,7 +1673,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberDouble": "1.234" } + "value": { + "$numberDouble": "1.234" + } }, "local_double_rand_auto_altname": { "kms": "local", @@ -1315,7 +1684,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$numberDouble": "1.234" } + "value": { + "$numberDouble": "1.234" + } }, "local_double_rand_explicit_id": { "kms": "local", @@ -1324,7 +1695,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberDouble": "1.234" } + "value": { + "$numberDouble": "1.234" + } }, "local_double_rand_explicit_altname": { "kms": "local", @@ -1333,7 +1706,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberDouble": "1.234" } + "value": { + "$numberDouble": "1.234" + } }, "local_double_det_explicit_id": { "kms": "local", @@ -1342,7 +1717,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$numberDouble": "1.234" } + "value": { + "$numberDouble": "1.234" + } }, "local_double_det_explicit_altname": { "kms": "local", @@ -1351,8 +1728,10 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$numberDouble": "1.234" } - }, + "value": { + "$numberDouble": "1.234" + } + }, "local_string_rand_auto_id": { "kms": "local", "type": "string", @@ -1423,7 +1802,11 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "local_object_rand_auto_altname": { "kms": "local", @@ -1432,7 +1815,11 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "local_object_rand_explicit_id": { "kms": "local", @@ -1441,7 +1828,11 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "local_object_rand_explicit_altname": { "kms": "local", @@ -1450,7 +1841,11 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "local_object_det_explicit_id": { "kms": "local", @@ -1459,7 +1854,11 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "local_object_det_explicit_altname": { "kms": "local", @@ -1468,7 +1867,11 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "x": { "$numberInt": "1" } } + "value": { + "x": { + "$numberInt": "1" + } + } }, "local_array_rand_auto_id": { "kms": "local", @@ -1478,9 +1881,15 @@ "identifier": "id", "allowed": true, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "local_array_rand_auto_altname": { @@ -1491,9 +1900,15 @@ "identifier": "altname", "allowed": true, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "local_array_rand_explicit_id": { @@ -1504,9 +1919,15 @@ "identifier": "id", "allowed": true, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "local_array_rand_explicit_altname": { @@ -1517,9 +1938,15 @@ "identifier": "altname", "allowed": true, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "local_array_det_explicit_id": { @@ -1530,9 +1957,15 @@ "identifier": "id", "allowed": false, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "local_array_det_explicit_altname": { @@ -1543,9 +1976,15 @@ "identifier": "altname", "allowed": false, "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } ] }, "local_binData=00_rand_auto_id": { @@ -1555,7 +1994,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "local_binData=00_rand_auto_altname": { "kms": "local", @@ -1564,7 +2008,12 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "local_binData=00_rand_explicit_id": { "kms": "local", @@ -1573,7 +2022,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "local_binData=00_rand_explicit_altname": { "kms": "local", @@ -1582,7 +2036,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "local_binData=00_det_auto_id": { "kms": "local", @@ -1591,7 +2050,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "local_binData=00_det_explicit_id": { "kms": "local", @@ -1600,7 +2064,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "local_binData=00_det_explicit_altname": { "kms": "local", @@ -1609,7 +2078,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$binary": { "base64": "AQIDBA==", "subType": "00" } } + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } }, "local_binData=04_rand_auto_id": { "kms": "local", @@ -1619,7 +2093,10 @@ "identifier": "id", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "local_binData=04_rand_auto_altname": { @@ -1630,7 +2107,10 @@ "identifier": "altname", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "local_binData=04_rand_explicit_id": { @@ -1641,7 +2121,10 @@ "identifier": "id", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "local_binData=04_rand_explicit_altname": { @@ -1652,7 +2135,10 @@ "identifier": "altname", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "local_binData=04_det_auto_id": { @@ -1663,7 +2149,10 @@ "identifier": "id", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "local_binData=04_det_explicit_id": { @@ -1674,7 +2163,10 @@ "identifier": "id", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "local_binData=04_det_explicit_altname": { @@ -1685,7 +2177,10 @@ "identifier": "altname", "allowed": true, "value": { - "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } } }, "local_undefined_rand_explicit_id": { @@ -1695,7 +2190,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$undefined": true } + "value": { + "$undefined": true + } }, "local_undefined_rand_explicit_altname": { "kms": "local", @@ -1704,7 +2201,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$undefined": true } + "value": { + "$undefined": true + } }, "local_undefined_det_explicit_id": { "kms": "local", @@ -1713,7 +2212,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$undefined": true } + "value": { + "$undefined": true + } }, "local_undefined_det_explicit_altname": { "kms": "local", @@ -1722,7 +2223,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$undefined": true } + "value": { + "$undefined": true + } }, "local_objectId_rand_auto_id": { "kms": "local", @@ -1731,7 +2234,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "local_objectId_rand_auto_altname": { "kms": "local", @@ -1740,7 +2245,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "local_objectId_rand_explicit_id": { "kms": "local", @@ -1749,7 +2256,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "local_objectId_rand_explicit_altname": { "kms": "local", @@ -1758,7 +2267,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "local_objectId_det_auto_id": { "kms": "local", @@ -1767,7 +2278,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "local_objectId_det_explicit_id": { "kms": "local", @@ -1776,7 +2289,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "local_objectId_det_explicit_altname": { "kms": "local", @@ -1785,7 +2300,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$oid": "01234567890abcdef0123456" } + "value": { + "$oid": "01234567890abcdef0123456" + } }, "local_bool_rand_auto_id": { "kms": "local", @@ -1848,7 +2365,11 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "local_date_rand_auto_altname": { "kms": "local", @@ -1857,7 +2378,11 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "local_date_rand_explicit_id": { "kms": "local", @@ -1866,7 +2391,11 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "local_date_rand_explicit_altname": { "kms": "local", @@ -1875,7 +2404,11 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "local_date_det_auto_id": { "kms": "local", @@ -1884,7 +2417,11 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "local_date_det_explicit_id": { "kms": "local", @@ -1893,7 +2430,11 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "local_date_det_explicit_altname": { "kms": "local", @@ -1902,7 +2443,11 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$date": { "$numberLong": "12345" } } + "value": { + "$date": { + "$numberLong": "12345" + } + } }, "local_null_rand_explicit_id": { "kms": "local", @@ -1947,7 +2492,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "local_regex_rand_auto_altname": { "kms": "local", @@ -1956,7 +2506,12 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "local_regex_rand_explicit_id": { "kms": "local", @@ -1965,7 +2520,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "local_regex_rand_explicit_altname": { "kms": "local", @@ -1974,7 +2534,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "local_regex_det_auto_id": { "kms": "local", @@ -1983,7 +2548,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "local_regex_det_explicit_id": { "kms": "local", @@ -1992,7 +2562,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "local_regex_det_explicit_altname": { "kms": "local", @@ -2001,7 +2576,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$regularExpression": { "pattern": ".*", "options": "" } } + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } }, "local_dbPointer_rand_auto_id": { "kms": "local", @@ -2013,7 +2593,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -2027,7 +2609,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -2041,7 +2625,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -2055,7 +2641,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -2069,7 +2657,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -2083,7 +2673,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -2097,7 +2689,9 @@ "value": { "$dbPointer": { "$ref": "db.example", - "$id": { "$oid": "01234567890abcdef0123456" } + "$id": { + "$oid": "01234567890abcdef0123456" + } } } }, @@ -2108,7 +2702,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "local_javascript_rand_auto_altname": { "kms": "local", @@ -2117,7 +2713,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "local_javascript_rand_explicit_id": { "kms": "local", @@ -2126,7 +2724,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "local_javascript_rand_explicit_altname": { "kms": "local", @@ -2135,7 +2735,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "local_javascript_det_auto_id": { "kms": "local", @@ -2144,7 +2746,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "local_javascript_det_explicit_id": { "kms": "local", @@ -2153,7 +2757,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "local_javascript_det_explicit_altname": { "kms": "local", @@ -2162,7 +2768,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$code": "x=1" } + "value": { + "$code": "x=1" + } }, "local_symbol_rand_auto_id": { "kms": "local", @@ -2171,7 +2779,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "local_symbol_rand_auto_altname": { "kms": "local", @@ -2180,7 +2790,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "local_symbol_rand_explicit_id": { "kms": "local", @@ -2189,7 +2801,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "local_symbol_rand_explicit_altname": { "kms": "local", @@ -2198,7 +2812,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "local_symbol_det_auto_id": { "kms": "local", @@ -2207,7 +2823,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "local_symbol_det_explicit_id": { "kms": "local", @@ -2216,7 +2834,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "local_symbol_det_explicit_altname": { "kms": "local", @@ -2225,7 +2845,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$symbol": "mongodb-symbol" } + "value": { + "$symbol": "mongodb-symbol" + } }, "local_javascriptWithScope_rand_auto_id": { "kms": "local", @@ -2234,7 +2856,10 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "local_javascriptWithScope_rand_auto_altname": { "kms": "local", @@ -2243,7 +2868,10 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "local_javascriptWithScope_rand_explicit_id": { "kms": "local", @@ -2252,7 +2880,10 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "local_javascriptWithScope_rand_explicit_altname": { "kms": "local", @@ -2261,7 +2892,10 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "local_javascriptWithScope_det_explicit_id": { "kms": "local", @@ -2270,7 +2904,10 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "local_javascriptWithScope_det_explicit_altname": { "kms": "local", @@ -2279,7 +2916,10 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$code": "x=1", "$scope": {} } + "value": { + "$code": "x=1", + "$scope": {} + } }, "local_int_rand_auto_id": { "kms": "local", @@ -2288,7 +2928,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "local_int_rand_auto_altname": { "kms": "local", @@ -2297,7 +2939,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "local_int_rand_explicit_id": { "kms": "local", @@ -2306,7 +2950,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "local_int_rand_explicit_altname": { "kms": "local", @@ -2315,7 +2961,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "local_int_det_auto_id": { "kms": "local", @@ -2324,7 +2972,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "local_int_det_explicit_id": { "kms": "local", @@ -2333,7 +2983,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "local_int_det_explicit_altname": { "kms": "local", @@ -2342,7 +2994,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberInt": "123" } + "value": { + "$numberInt": "123" + } }, "local_timestamp_rand_auto_id": { "kms": "local", @@ -2351,7 +3005,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "local_timestamp_rand_auto_altname": { "kms": "local", @@ -2360,7 +3019,12 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "local_timestamp_rand_explicit_id": { "kms": "local", @@ -2369,7 +3033,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "local_timestamp_rand_explicit_altname": { "kms": "local", @@ -2378,7 +3047,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "local_timestamp_det_auto_id": { "kms": "local", @@ -2387,7 +3061,12 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "local_timestamp_det_explicit_id": { "kms": "local", @@ -2396,7 +3075,12 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "local_timestamp_det_explicit_altname": { "kms": "local", @@ -2405,7 +3089,12 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$timestamp": { "t": 0, "i": 12345 } } + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } }, "local_long_rand_auto_id": { "kms": "local", @@ -2414,7 +3103,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "local_long_rand_auto_altname": { "kms": "local", @@ -2423,7 +3114,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "local_long_rand_explicit_id": { "kms": "local", @@ -2432,7 +3125,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "local_long_rand_explicit_altname": { "kms": "local", @@ -2441,7 +3136,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "local_long_det_auto_id": { "kms": "local", @@ -2450,7 +3147,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "local_long_det_explicit_id": { "kms": "local", @@ -2459,7 +3158,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "local_long_det_explicit_altname": { "kms": "local", @@ -2468,7 +3169,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberLong": "456" } + "value": { + "$numberLong": "456" + } }, "local_decimal_rand_auto_id": { "kms": "local", @@ -2477,7 +3180,9 @@ "method": "auto", "identifier": "id", "allowed": true, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "local_decimal_rand_auto_altname": { "kms": "local", @@ -2486,7 +3191,9 @@ "method": "auto", "identifier": "altname", "allowed": true, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "local_decimal_rand_explicit_id": { "kms": "local", @@ -2495,7 +3202,9 @@ "method": "explicit", "identifier": "id", "allowed": true, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "local_decimal_rand_explicit_altname": { "kms": "local", @@ -2504,7 +3213,9 @@ "method": "explicit", "identifier": "altname", "allowed": true, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "local_decimal_det_explicit_id": { "kms": "local", @@ -2513,7 +3224,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "local_decimal_det_explicit_altname": { "kms": "local", @@ -2522,7 +3235,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$numberDecimal": "1.234" } + "value": { + "$numberDecimal": "1.234" + } }, "local_minKey_rand_explicit_id": { "kms": "local", @@ -2531,7 +3246,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$minKey": 1 } + "value": { + "$minKey": 1 + } }, "local_minKey_rand_explicit_altname": { "kms": "local", @@ -2540,7 +3257,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$minKey": 1 } + "value": { + "$minKey": 1 + } }, "local_minKey_det_explicit_id": { "kms": "local", @@ -2549,7 +3268,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$minKey": 1 } + "value": { + "$minKey": 1 + } }, "local_minKey_det_explicit_altname": { "kms": "local", @@ -2558,7 +3279,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$minKey": 1 } + "value": { + "$minKey": 1 + } }, "local_maxKey_rand_explicit_id": { "kms": "local", @@ -2567,7 +3290,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$maxKey": 1 } + "value": { + "$maxKey": 1 + } }, "local_maxKey_rand_explicit_altname": { "kms": "local", @@ -2576,7 +3301,9 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$maxKey": 1 } + "value": { + "$maxKey": 1 + } }, "local_maxKey_det_explicit_id": { "kms": "local", @@ -2585,7 +3312,9 @@ "method": "explicit", "identifier": "id", "allowed": false, - "value": { "$maxKey": 1 } + "value": { + "$maxKey": 1 + } }, "local_maxKey_det_explicit_altname": { "kms": "local", @@ -2594,7 +3323,4992 @@ "method": "explicit", "identifier": "altname", "allowed": false, - "value": { "$maxKey": 1 } + "value": { + "$maxKey": 1 + } + }, + "azure_double_rand_auto_id": { + "kms": "azure", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "azure_double_rand_auto_altname": { + "kms": "azure", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "azure_double_rand_explicit_id": { + "kms": "azure", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "azure_double_rand_explicit_altname": { + "kms": "azure", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "azure_double_det_explicit_id": { + "kms": "azure", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDouble": "1.234" + } + }, + "azure_double_det_explicit_altname": { + "kms": "azure", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDouble": "1.234" + } + }, + "azure_string_rand_auto_id": { + "kms": "azure", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "azure_string_rand_auto_altname": { + "kms": "azure", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "azure_string_rand_explicit_id": { + "kms": "azure", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "azure_string_rand_explicit_altname": { + "kms": "azure", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "azure_string_det_auto_id": { + "kms": "azure", + "type": "string", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "azure_string_det_explicit_id": { + "kms": "azure", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "azure_string_det_explicit_altname": { + "kms": "azure", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "azure_object_rand_auto_id": { + "kms": "azure", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "azure_object_rand_auto_altname": { + "kms": "azure", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "azure_object_rand_explicit_id": { + "kms": "azure", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "azure_object_rand_explicit_altname": { + "kms": "azure", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "azure_object_det_explicit_id": { + "kms": "azure", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "azure_object_det_explicit_altname": { + "kms": "azure", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "azure_array_rand_auto_id": { + "kms": "azure", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "azure_array_rand_auto_altname": { + "kms": "azure", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "azure_array_rand_explicit_id": { + "kms": "azure", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "azure_array_rand_explicit_altname": { + "kms": "azure", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "azure_array_det_explicit_id": { + "kms": "azure", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "azure_array_det_explicit_altname": { + "kms": "azure", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "azure_binData=00_rand_auto_id": { + "kms": "azure", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "azure_binData=00_rand_auto_altname": { + "kms": "azure", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "azure_binData=00_rand_explicit_id": { + "kms": "azure", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "azure_binData=00_rand_explicit_altname": { + "kms": "azure", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "azure_binData=00_det_auto_id": { + "kms": "azure", + "type": "binData=00", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "azure_binData=00_det_explicit_id": { + "kms": "azure", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "azure_binData=00_det_explicit_altname": { + "kms": "azure", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "azure_binData=04_rand_auto_id": { + "kms": "azure", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "azure_binData=04_rand_auto_altname": { + "kms": "azure", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "azure_binData=04_rand_explicit_id": { + "kms": "azure", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "azure_binData=04_rand_explicit_altname": { + "kms": "azure", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "azure_binData=04_det_auto_id": { + "kms": "azure", + "type": "binData=04", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "azure_binData=04_det_explicit_id": { + "kms": "azure", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "azure_binData=04_det_explicit_altname": { + "kms": "azure", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "azure_undefined_rand_explicit_id": { + "kms": "azure", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "azure_undefined_rand_explicit_altname": { + "kms": "azure", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "azure_undefined_det_explicit_id": { + "kms": "azure", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "azure_undefined_det_explicit_altname": { + "kms": "azure", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "azure_objectId_rand_auto_id": { + "kms": "azure", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "azure_objectId_rand_auto_altname": { + "kms": "azure", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "azure_objectId_rand_explicit_id": { + "kms": "azure", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "azure_objectId_rand_explicit_altname": { + "kms": "azure", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "azure_objectId_det_auto_id": { + "kms": "azure", + "type": "objectId", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "azure_objectId_det_explicit_id": { + "kms": "azure", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "azure_objectId_det_explicit_altname": { + "kms": "azure", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "azure_bool_rand_auto_id": { + "kms": "azure", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": true + }, + "azure_bool_rand_auto_altname": { + "kms": "azure", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": true + }, + "azure_bool_rand_explicit_id": { + "kms": "azure", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": true + }, + "azure_bool_rand_explicit_altname": { + "kms": "azure", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": true + }, + "azure_bool_det_explicit_id": { + "kms": "azure", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": true + }, + "azure_bool_det_explicit_altname": { + "kms": "azure", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": true + }, + "azure_date_rand_auto_id": { + "kms": "azure", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "azure_date_rand_auto_altname": { + "kms": "azure", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "azure_date_rand_explicit_id": { + "kms": "azure", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "azure_date_rand_explicit_altname": { + "kms": "azure", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "azure_date_det_auto_id": { + "kms": "azure", + "type": "date", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "azure_date_det_explicit_id": { + "kms": "azure", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "azure_date_det_explicit_altname": { + "kms": "azure", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "azure_null_rand_explicit_id": { + "kms": "azure", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "azure_null_rand_explicit_altname": { + "kms": "azure", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "azure_null_det_explicit_id": { + "kms": "azure", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "azure_null_det_explicit_altname": { + "kms": "azure", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "azure_regex_rand_auto_id": { + "kms": "azure", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "azure_regex_rand_auto_altname": { + "kms": "azure", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "azure_regex_rand_explicit_id": { + "kms": "azure", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "azure_regex_rand_explicit_altname": { + "kms": "azure", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "azure_regex_det_auto_id": { + "kms": "azure", + "type": "regex", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "azure_regex_det_explicit_id": { + "kms": "azure", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "azure_regex_det_explicit_altname": { + "kms": "azure", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "azure_dbPointer_rand_auto_id": { + "kms": "azure", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "azure_dbPointer_rand_auto_altname": { + "kms": "azure", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "azure_dbPointer_rand_explicit_id": { + "kms": "azure", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "azure_dbPointer_rand_explicit_altname": { + "kms": "azure", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "azure_dbPointer_det_auto_id": { + "kms": "azure", + "type": "dbPointer", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "azure_dbPointer_det_explicit_id": { + "kms": "azure", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "azure_dbPointer_det_explicit_altname": { + "kms": "azure", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "azure_javascript_rand_auto_id": { + "kms": "azure", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "azure_javascript_rand_auto_altname": { + "kms": "azure", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "azure_javascript_rand_explicit_id": { + "kms": "azure", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "azure_javascript_rand_explicit_altname": { + "kms": "azure", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "azure_javascript_det_auto_id": { + "kms": "azure", + "type": "javascript", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "azure_javascript_det_explicit_id": { + "kms": "azure", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "azure_javascript_det_explicit_altname": { + "kms": "azure", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "azure_symbol_rand_auto_id": { + "kms": "azure", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "azure_symbol_rand_auto_altname": { + "kms": "azure", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "azure_symbol_rand_explicit_id": { + "kms": "azure", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "azure_symbol_rand_explicit_altname": { + "kms": "azure", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "azure_symbol_det_auto_id": { + "kms": "azure", + "type": "symbol", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "azure_symbol_det_explicit_id": { + "kms": "azure", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "azure_symbol_det_explicit_altname": { + "kms": "azure", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "azure_javascriptWithScope_rand_auto_id": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "azure_javascriptWithScope_rand_auto_altname": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "azure_javascriptWithScope_rand_explicit_id": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "azure_javascriptWithScope_rand_explicit_altname": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "azure_javascriptWithScope_det_explicit_id": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "azure_javascriptWithScope_det_explicit_altname": { + "kms": "azure", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "azure_int_rand_auto_id": { + "kms": "azure", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "azure_int_rand_auto_altname": { + "kms": "azure", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "azure_int_rand_explicit_id": { + "kms": "azure", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "azure_int_rand_explicit_altname": { + "kms": "azure", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "azure_int_det_auto_id": { + "kms": "azure", + "type": "int", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "azure_int_det_explicit_id": { + "kms": "azure", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "azure_int_det_explicit_altname": { + "kms": "azure", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "azure_timestamp_rand_auto_id": { + "kms": "azure", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "azure_timestamp_rand_auto_altname": { + "kms": "azure", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "azure_timestamp_rand_explicit_id": { + "kms": "azure", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "azure_timestamp_rand_explicit_altname": { + "kms": "azure", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "azure_timestamp_det_auto_id": { + "kms": "azure", + "type": "timestamp", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "azure_timestamp_det_explicit_id": { + "kms": "azure", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "azure_timestamp_det_explicit_altname": { + "kms": "azure", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "azure_long_rand_auto_id": { + "kms": "azure", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "azure_long_rand_auto_altname": { + "kms": "azure", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "azure_long_rand_explicit_id": { + "kms": "azure", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "azure_long_rand_explicit_altname": { + "kms": "azure", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "azure_long_det_auto_id": { + "kms": "azure", + "type": "long", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "azure_long_det_explicit_id": { + "kms": "azure", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "azure_long_det_explicit_altname": { + "kms": "azure", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "azure_decimal_rand_auto_id": { + "kms": "azure", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "azure_decimal_rand_auto_altname": { + "kms": "azure", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "azure_decimal_rand_explicit_id": { + "kms": "azure", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "azure_decimal_rand_explicit_altname": { + "kms": "azure", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "azure_decimal_det_explicit_id": { + "kms": "azure", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "azure_decimal_det_explicit_altname": { + "kms": "azure", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "azure_minKey_rand_explicit_id": { + "kms": "azure", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "azure_minKey_rand_explicit_altname": { + "kms": "azure", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "azure_minKey_det_explicit_id": { + "kms": "azure", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "azure_minKey_det_explicit_altname": { + "kms": "azure", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "azure_maxKey_rand_explicit_id": { + "kms": "azure", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "azure_maxKey_rand_explicit_altname": { + "kms": "azure", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "azure_maxKey_det_explicit_id": { + "kms": "azure", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "azure_maxKey_det_explicit_altname": { + "kms": "azure", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "gcp_double_rand_auto_id": { + "kms": "gcp", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "gcp_double_rand_auto_altname": { + "kms": "gcp", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "gcp_double_rand_explicit_id": { + "kms": "gcp", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "gcp_double_rand_explicit_altname": { + "kms": "gcp", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "gcp_double_det_explicit_id": { + "kms": "gcp", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDouble": "1.234" + } + }, + "gcp_double_det_explicit_altname": { + "kms": "gcp", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDouble": "1.234" + } + }, + "gcp_string_rand_auto_id": { + "kms": "gcp", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "gcp_string_rand_auto_altname": { + "kms": "gcp", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "gcp_string_rand_explicit_id": { + "kms": "gcp", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "gcp_string_rand_explicit_altname": { + "kms": "gcp", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "gcp_string_det_auto_id": { + "kms": "gcp", + "type": "string", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "gcp_string_det_explicit_id": { + "kms": "gcp", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "gcp_string_det_explicit_altname": { + "kms": "gcp", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "gcp_object_rand_auto_id": { + "kms": "gcp", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "gcp_object_rand_auto_altname": { + "kms": "gcp", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "gcp_object_rand_explicit_id": { + "kms": "gcp", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "gcp_object_rand_explicit_altname": { + "kms": "gcp", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "gcp_object_det_explicit_id": { + "kms": "gcp", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "gcp_object_det_explicit_altname": { + "kms": "gcp", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "gcp_array_rand_auto_id": { + "kms": "gcp", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "gcp_array_rand_auto_altname": { + "kms": "gcp", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "gcp_array_rand_explicit_id": { + "kms": "gcp", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "gcp_array_rand_explicit_altname": { + "kms": "gcp", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "gcp_array_det_explicit_id": { + "kms": "gcp", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "gcp_array_det_explicit_altname": { + "kms": "gcp", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "gcp_binData=00_rand_auto_id": { + "kms": "gcp", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "gcp_binData=00_rand_auto_altname": { + "kms": "gcp", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "gcp_binData=00_rand_explicit_id": { + "kms": "gcp", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "gcp_binData=00_rand_explicit_altname": { + "kms": "gcp", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "gcp_binData=00_det_auto_id": { + "kms": "gcp", + "type": "binData=00", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "gcp_binData=00_det_explicit_id": { + "kms": "gcp", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "gcp_binData=00_det_explicit_altname": { + "kms": "gcp", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "gcp_binData=04_rand_auto_id": { + "kms": "gcp", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "gcp_binData=04_rand_auto_altname": { + "kms": "gcp", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "gcp_binData=04_rand_explicit_id": { + "kms": "gcp", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "gcp_binData=04_rand_explicit_altname": { + "kms": "gcp", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "gcp_binData=04_det_auto_id": { + "kms": "gcp", + "type": "binData=04", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "gcp_binData=04_det_explicit_id": { + "kms": "gcp", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "gcp_binData=04_det_explicit_altname": { + "kms": "gcp", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "gcp_undefined_rand_explicit_id": { + "kms": "gcp", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "gcp_undefined_rand_explicit_altname": { + "kms": "gcp", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "gcp_undefined_det_explicit_id": { + "kms": "gcp", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "gcp_undefined_det_explicit_altname": { + "kms": "gcp", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "gcp_objectId_rand_auto_id": { + "kms": "gcp", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "gcp_objectId_rand_auto_altname": { + "kms": "gcp", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "gcp_objectId_rand_explicit_id": { + "kms": "gcp", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "gcp_objectId_rand_explicit_altname": { + "kms": "gcp", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "gcp_objectId_det_auto_id": { + "kms": "gcp", + "type": "objectId", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "gcp_objectId_det_explicit_id": { + "kms": "gcp", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "gcp_objectId_det_explicit_altname": { + "kms": "gcp", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "gcp_bool_rand_auto_id": { + "kms": "gcp", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": true + }, + "gcp_bool_rand_auto_altname": { + "kms": "gcp", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": true + }, + "gcp_bool_rand_explicit_id": { + "kms": "gcp", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": true + }, + "gcp_bool_rand_explicit_altname": { + "kms": "gcp", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": true + }, + "gcp_bool_det_explicit_id": { + "kms": "gcp", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": true + }, + "gcp_bool_det_explicit_altname": { + "kms": "gcp", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": true + }, + "gcp_date_rand_auto_id": { + "kms": "gcp", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "gcp_date_rand_auto_altname": { + "kms": "gcp", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "gcp_date_rand_explicit_id": { + "kms": "gcp", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "gcp_date_rand_explicit_altname": { + "kms": "gcp", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "gcp_date_det_auto_id": { + "kms": "gcp", + "type": "date", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "gcp_date_det_explicit_id": { + "kms": "gcp", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "gcp_date_det_explicit_altname": { + "kms": "gcp", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "gcp_null_rand_explicit_id": { + "kms": "gcp", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "gcp_null_rand_explicit_altname": { + "kms": "gcp", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "gcp_null_det_explicit_id": { + "kms": "gcp", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "gcp_null_det_explicit_altname": { + "kms": "gcp", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "gcp_regex_rand_auto_id": { + "kms": "gcp", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "gcp_regex_rand_auto_altname": { + "kms": "gcp", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "gcp_regex_rand_explicit_id": { + "kms": "gcp", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "gcp_regex_rand_explicit_altname": { + "kms": "gcp", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "gcp_regex_det_auto_id": { + "kms": "gcp", + "type": "regex", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "gcp_regex_det_explicit_id": { + "kms": "gcp", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "gcp_regex_det_explicit_altname": { + "kms": "gcp", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "gcp_dbPointer_rand_auto_id": { + "kms": "gcp", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "gcp_dbPointer_rand_auto_altname": { + "kms": "gcp", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "gcp_dbPointer_rand_explicit_id": { + "kms": "gcp", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "gcp_dbPointer_rand_explicit_altname": { + "kms": "gcp", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "gcp_dbPointer_det_auto_id": { + "kms": "gcp", + "type": "dbPointer", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "gcp_dbPointer_det_explicit_id": { + "kms": "gcp", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "gcp_dbPointer_det_explicit_altname": { + "kms": "gcp", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "gcp_javascript_rand_auto_id": { + "kms": "gcp", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "gcp_javascript_rand_auto_altname": { + "kms": "gcp", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "gcp_javascript_rand_explicit_id": { + "kms": "gcp", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "gcp_javascript_rand_explicit_altname": { + "kms": "gcp", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "gcp_javascript_det_auto_id": { + "kms": "gcp", + "type": "javascript", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "gcp_javascript_det_explicit_id": { + "kms": "gcp", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "gcp_javascript_det_explicit_altname": { + "kms": "gcp", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "gcp_symbol_rand_auto_id": { + "kms": "gcp", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "gcp_symbol_rand_auto_altname": { + "kms": "gcp", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "gcp_symbol_rand_explicit_id": { + "kms": "gcp", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "gcp_symbol_rand_explicit_altname": { + "kms": "gcp", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "gcp_symbol_det_auto_id": { + "kms": "gcp", + "type": "symbol", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "gcp_symbol_det_explicit_id": { + "kms": "gcp", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "gcp_symbol_det_explicit_altname": { + "kms": "gcp", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "gcp_javascriptWithScope_rand_auto_id": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "gcp_javascriptWithScope_rand_auto_altname": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "gcp_javascriptWithScope_rand_explicit_id": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "gcp_javascriptWithScope_rand_explicit_altname": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "gcp_javascriptWithScope_det_explicit_id": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "gcp_javascriptWithScope_det_explicit_altname": { + "kms": "gcp", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "gcp_int_rand_auto_id": { + "kms": "gcp", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "gcp_int_rand_auto_altname": { + "kms": "gcp", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "gcp_int_rand_explicit_id": { + "kms": "gcp", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "gcp_int_rand_explicit_altname": { + "kms": "gcp", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "gcp_int_det_auto_id": { + "kms": "gcp", + "type": "int", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "gcp_int_det_explicit_id": { + "kms": "gcp", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "gcp_int_det_explicit_altname": { + "kms": "gcp", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "gcp_timestamp_rand_auto_id": { + "kms": "gcp", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "gcp_timestamp_rand_auto_altname": { + "kms": "gcp", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "gcp_timestamp_rand_explicit_id": { + "kms": "gcp", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "gcp_timestamp_rand_explicit_altname": { + "kms": "gcp", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "gcp_timestamp_det_auto_id": { + "kms": "gcp", + "type": "timestamp", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "gcp_timestamp_det_explicit_id": { + "kms": "gcp", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "gcp_timestamp_det_explicit_altname": { + "kms": "gcp", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "gcp_long_rand_auto_id": { + "kms": "gcp", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "gcp_long_rand_auto_altname": { + "kms": "gcp", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "gcp_long_rand_explicit_id": { + "kms": "gcp", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "gcp_long_rand_explicit_altname": { + "kms": "gcp", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "gcp_long_det_auto_id": { + "kms": "gcp", + "type": "long", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "gcp_long_det_explicit_id": { + "kms": "gcp", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "gcp_long_det_explicit_altname": { + "kms": "gcp", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "gcp_decimal_rand_auto_id": { + "kms": "gcp", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "gcp_decimal_rand_auto_altname": { + "kms": "gcp", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "gcp_decimal_rand_explicit_id": { + "kms": "gcp", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "gcp_decimal_rand_explicit_altname": { + "kms": "gcp", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "gcp_decimal_det_explicit_id": { + "kms": "gcp", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "gcp_decimal_det_explicit_altname": { + "kms": "gcp", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "gcp_minKey_rand_explicit_id": { + "kms": "gcp", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "gcp_minKey_rand_explicit_altname": { + "kms": "gcp", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "gcp_minKey_det_explicit_id": { + "kms": "gcp", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "gcp_minKey_det_explicit_altname": { + "kms": "gcp", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "gcp_maxKey_rand_explicit_id": { + "kms": "gcp", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "gcp_maxKey_rand_explicit_altname": { + "kms": "gcp", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "gcp_maxKey_det_explicit_id": { + "kms": "gcp", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "gcp_maxKey_det_explicit_altname": { + "kms": "gcp", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "kmip_double_rand_auto_id": { + "kms": "kmip", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "kmip_double_rand_auto_altname": { + "kms": "kmip", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "kmip_double_rand_explicit_id": { + "kms": "kmip", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "kmip_double_rand_explicit_altname": { + "kms": "kmip", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "kmip_double_det_explicit_id": { + "kms": "kmip", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDouble": "1.234" + } + }, + "kmip_double_det_explicit_altname": { + "kms": "kmip", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDouble": "1.234" + } + }, + "kmip_string_rand_auto_id": { + "kms": "kmip", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "kmip_string_rand_auto_altname": { + "kms": "kmip", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "kmip_string_rand_explicit_id": { + "kms": "kmip", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "kmip_string_rand_explicit_altname": { + "kms": "kmip", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "kmip_string_det_auto_id": { + "kms": "kmip", + "type": "string", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "kmip_string_det_explicit_id": { + "kms": "kmip", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "kmip_string_det_explicit_altname": { + "kms": "kmip", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "kmip_object_rand_auto_id": { + "kms": "kmip", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "kmip_object_rand_auto_altname": { + "kms": "kmip", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "kmip_object_rand_explicit_id": { + "kms": "kmip", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "kmip_object_rand_explicit_altname": { + "kms": "kmip", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "kmip_object_det_explicit_id": { + "kms": "kmip", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "kmip_object_det_explicit_altname": { + "kms": "kmip", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "kmip_array_rand_auto_id": { + "kms": "kmip", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "kmip_array_rand_auto_altname": { + "kms": "kmip", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "kmip_array_rand_explicit_id": { + "kms": "kmip", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "kmip_array_rand_explicit_altname": { + "kms": "kmip", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "kmip_array_det_explicit_id": { + "kms": "kmip", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "kmip_array_det_explicit_altname": { + "kms": "kmip", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "kmip_binData=00_rand_auto_id": { + "kms": "kmip", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "kmip_binData=00_rand_auto_altname": { + "kms": "kmip", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "kmip_binData=00_rand_explicit_id": { + "kms": "kmip", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "kmip_binData=00_rand_explicit_altname": { + "kms": "kmip", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "kmip_binData=00_det_auto_id": { + "kms": "kmip", + "type": "binData=00", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "kmip_binData=00_det_explicit_id": { + "kms": "kmip", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "kmip_binData=00_det_explicit_altname": { + "kms": "kmip", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "kmip_binData=04_rand_auto_id": { + "kms": "kmip", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "kmip_binData=04_rand_auto_altname": { + "kms": "kmip", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "kmip_binData=04_rand_explicit_id": { + "kms": "kmip", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "kmip_binData=04_rand_explicit_altname": { + "kms": "kmip", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "kmip_binData=04_det_auto_id": { + "kms": "kmip", + "type": "binData=04", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "kmip_binData=04_det_explicit_id": { + "kms": "kmip", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "kmip_binData=04_det_explicit_altname": { + "kms": "kmip", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "kmip_undefined_rand_explicit_id": { + "kms": "kmip", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "kmip_undefined_rand_explicit_altname": { + "kms": "kmip", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "kmip_undefined_det_explicit_id": { + "kms": "kmip", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "kmip_undefined_det_explicit_altname": { + "kms": "kmip", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "kmip_objectId_rand_auto_id": { + "kms": "kmip", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "kmip_objectId_rand_auto_altname": { + "kms": "kmip", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "kmip_objectId_rand_explicit_id": { + "kms": "kmip", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "kmip_objectId_rand_explicit_altname": { + "kms": "kmip", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "kmip_objectId_det_auto_id": { + "kms": "kmip", + "type": "objectId", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "kmip_objectId_det_explicit_id": { + "kms": "kmip", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "kmip_objectId_det_explicit_altname": { + "kms": "kmip", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "kmip_bool_rand_auto_id": { + "kms": "kmip", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": true + }, + "kmip_bool_rand_auto_altname": { + "kms": "kmip", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": true + }, + "kmip_bool_rand_explicit_id": { + "kms": "kmip", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": true + }, + "kmip_bool_rand_explicit_altname": { + "kms": "kmip", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": true + }, + "kmip_bool_det_explicit_id": { + "kms": "kmip", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": true + }, + "kmip_bool_det_explicit_altname": { + "kms": "kmip", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": true + }, + "kmip_date_rand_auto_id": { + "kms": "kmip", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "kmip_date_rand_auto_altname": { + "kms": "kmip", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "kmip_date_rand_explicit_id": { + "kms": "kmip", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "kmip_date_rand_explicit_altname": { + "kms": "kmip", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "kmip_date_det_auto_id": { + "kms": "kmip", + "type": "date", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "kmip_date_det_explicit_id": { + "kms": "kmip", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "kmip_date_det_explicit_altname": { + "kms": "kmip", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "kmip_null_rand_explicit_id": { + "kms": "kmip", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "kmip_null_rand_explicit_altname": { + "kms": "kmip", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "kmip_null_det_explicit_id": { + "kms": "kmip", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "kmip_null_det_explicit_altname": { + "kms": "kmip", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "kmip_regex_rand_auto_id": { + "kms": "kmip", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "kmip_regex_rand_auto_altname": { + "kms": "kmip", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "kmip_regex_rand_explicit_id": { + "kms": "kmip", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "kmip_regex_rand_explicit_altname": { + "kms": "kmip", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "kmip_regex_det_auto_id": { + "kms": "kmip", + "type": "regex", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "kmip_regex_det_explicit_id": { + "kms": "kmip", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "kmip_regex_det_explicit_altname": { + "kms": "kmip", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "kmip_dbPointer_rand_auto_id": { + "kms": "kmip", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "kmip_dbPointer_rand_auto_altname": { + "kms": "kmip", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "kmip_dbPointer_rand_explicit_id": { + "kms": "kmip", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "kmip_dbPointer_rand_explicit_altname": { + "kms": "kmip", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "kmip_dbPointer_det_auto_id": { + "kms": "kmip", + "type": "dbPointer", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "kmip_dbPointer_det_explicit_id": { + "kms": "kmip", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "kmip_dbPointer_det_explicit_altname": { + "kms": "kmip", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "kmip_javascript_rand_auto_id": { + "kms": "kmip", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "kmip_javascript_rand_auto_altname": { + "kms": "kmip", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "kmip_javascript_rand_explicit_id": { + "kms": "kmip", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "kmip_javascript_rand_explicit_altname": { + "kms": "kmip", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "kmip_javascript_det_auto_id": { + "kms": "kmip", + "type": "javascript", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "kmip_javascript_det_explicit_id": { + "kms": "kmip", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "kmip_javascript_det_explicit_altname": { + "kms": "kmip", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "kmip_symbol_rand_auto_id": { + "kms": "kmip", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "kmip_symbol_rand_auto_altname": { + "kms": "kmip", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "kmip_symbol_rand_explicit_id": { + "kms": "kmip", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "kmip_symbol_rand_explicit_altname": { + "kms": "kmip", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "kmip_symbol_det_auto_id": { + "kms": "kmip", + "type": "symbol", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "kmip_symbol_det_explicit_id": { + "kms": "kmip", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "kmip_symbol_det_explicit_altname": { + "kms": "kmip", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "kmip_javascriptWithScope_rand_auto_id": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "kmip_javascriptWithScope_rand_auto_altname": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "kmip_javascriptWithScope_rand_explicit_id": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "kmip_javascriptWithScope_rand_explicit_altname": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "kmip_javascriptWithScope_det_explicit_id": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "kmip_javascriptWithScope_det_explicit_altname": { + "kms": "kmip", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "kmip_int_rand_auto_id": { + "kms": "kmip", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "kmip_int_rand_auto_altname": { + "kms": "kmip", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "kmip_int_rand_explicit_id": { + "kms": "kmip", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "kmip_int_rand_explicit_altname": { + "kms": "kmip", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "kmip_int_det_auto_id": { + "kms": "kmip", + "type": "int", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "kmip_int_det_explicit_id": { + "kms": "kmip", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "kmip_int_det_explicit_altname": { + "kms": "kmip", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "kmip_timestamp_rand_auto_id": { + "kms": "kmip", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "kmip_timestamp_rand_auto_altname": { + "kms": "kmip", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "kmip_timestamp_rand_explicit_id": { + "kms": "kmip", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "kmip_timestamp_rand_explicit_altname": { + "kms": "kmip", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "kmip_timestamp_det_auto_id": { + "kms": "kmip", + "type": "timestamp", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "kmip_timestamp_det_explicit_id": { + "kms": "kmip", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "kmip_timestamp_det_explicit_altname": { + "kms": "kmip", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "kmip_long_rand_auto_id": { + "kms": "kmip", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "kmip_long_rand_auto_altname": { + "kms": "kmip", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "kmip_long_rand_explicit_id": { + "kms": "kmip", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "kmip_long_rand_explicit_altname": { + "kms": "kmip", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "kmip_long_det_auto_id": { + "kms": "kmip", + "type": "long", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "kmip_long_det_explicit_id": { + "kms": "kmip", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "kmip_long_det_explicit_altname": { + "kms": "kmip", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "kmip_decimal_rand_auto_id": { + "kms": "kmip", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "kmip_decimal_rand_auto_altname": { + "kms": "kmip", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "kmip_decimal_rand_explicit_id": { + "kms": "kmip", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "kmip_decimal_rand_explicit_altname": { + "kms": "kmip", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "kmip_decimal_det_explicit_id": { + "kms": "kmip", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "kmip_decimal_det_explicit_altname": { + "kms": "kmip", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "kmip_minKey_rand_explicit_id": { + "kms": "kmip", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "kmip_minKey_rand_explicit_altname": { + "kms": "kmip", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "kmip_minKey_det_explicit_id": { + "kms": "kmip", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "kmip_minKey_det_explicit_altname": { + "kms": "kmip", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "kmip_maxKey_rand_explicit_id": { + "kms": "kmip", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "kmip_maxKey_rand_explicit_altname": { + "kms": "kmip", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "kmip_maxKey_det_explicit_id": { + "kms": "kmip", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "kmip_maxKey_det_explicit_altname": { + "kms": "kmip", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } }, "payload=0,algo=rand": { "kms": "local", @@ -2902,4 +8616,4 @@ "allowed": true, "value": "aaaaaaaaaaaaaaaa" } -} +} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/etc/data/encryptedFields.json b/tests/SpecTests/client-side-encryption/etc/data/encryptedFields.json new file mode 100644 index 000000000..88abe5a60 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/encryptedFields.json @@ -0,0 +1,30 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] +} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/etc/data/keys/key1-document.json b/tests/SpecTests/client-side-encryption/etc/data/keys/key1-document.json new file mode 100644 index 000000000..566b56c35 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/keys/key1-document.json @@ -0,0 +1,30 @@ +{ + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } +} diff --git a/tests/SpecTests/client-side-encryption/etc/data/keys/key1-id.json b/tests/SpecTests/client-side-encryption/etc/data/keys/key1-id.json new file mode 100644 index 000000000..7d18f52eb --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/keys/key1-id.json @@ -0,0 +1,6 @@ +{ + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } +} diff --git a/tests/SpecTests/client-side-encryption/etc/data/keys/key2-document.json b/tests/SpecTests/client-side-encryption/etc/data/keys/key2-document.json new file mode 100644 index 000000000..a654d980b --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/keys/key2-document.json @@ -0,0 +1,30 @@ +{ + "_id": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "HBk9BWihXExNDvTp1lUxOuxuZK2Pe2ZdVdlsxPEBkiO1bS4mG5NNDsQ7zVxJAH8BtdOYp72Ku4Y3nwc0BUpIKsvAKX4eYXtlhv5zUQxWdeNFhg9qK7qb8nqhnnLeT0f25jFSqzWJoT379hfwDeu0bebJHr35QrJ8myZdPMTEDYF08QYQ48ShRBli0S+QzBHHAQiM2iJNr4svg2WR8JSeWQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } +} diff --git a/tests/SpecTests/client-side-encryption/etc/data/keys/key2-id.json b/tests/SpecTests/client-side-encryption/etc/data/keys/key2-id.json new file mode 100644 index 000000000..6e9b87bbc --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/keys/key2-id.json @@ -0,0 +1,6 @@ +{ + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } +} diff --git a/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-Date.json b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-Date.json new file mode 100644 index 000000000..97a2b2d4e --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-Date.json @@ -0,0 +1,33 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DecimalNoPrecision.json b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DecimalNoPrecision.json new file mode 100644 index 000000000..4d284475f --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DecimalNoPrecision.json @@ -0,0 +1,23 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DecimalPrecision.json b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DecimalPrecision.json new file mode 100644 index 000000000..53449182b --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DecimalPrecision.json @@ -0,0 +1,32 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DoubleNoPrecision.json b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DoubleNoPrecision.json new file mode 100644 index 000000000..b478a772d --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DoubleNoPrecision.json @@ -0,0 +1,23 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DoublePrecision.json b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DoublePrecision.json new file mode 100644 index 000000000..395a36968 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-DoublePrecision.json @@ -0,0 +1,32 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-Int.json b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-Int.json new file mode 100644 index 000000000..61b7082df --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-Int.json @@ -0,0 +1,29 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-Long.json b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-Long.json new file mode 100644 index 000000000..b18b84b6e --- /dev/null +++ b/tests/SpecTests/client-side-encryption/etc/data/range-encryptedFields-Long.json @@ -0,0 +1,29 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/external/external-key.json b/tests/SpecTests/client-side-encryption/external/external-key.json index 5da84ab6b..b3fe0723b 100644 --- a/tests/SpecTests/client-side-encryption/external/external-key.json +++ b/tests/SpecTests/client-side-encryption/external/external-key.json @@ -1,27 +1,27 @@ { "status": { "$numberInt": "1" - }, + }, "_id": { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } - }, + }, "masterKey": { "provider": "local" - }, + }, "updateDate": { "$date": { "$numberLong": "1557827033449" } - }, + }, "keyMaterial": { "$binary": { - "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", + "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", "subType": "00" } - }, + }, "creationDate": { "$date": { "$numberLong": "1557827033449" diff --git a/tests/SpecTests/client-side-encryption/external/external-schema.json b/tests/SpecTests/client-side-encryption/external/external-schema.json index 52f524a18..7d8cad8c3 100644 --- a/tests/SpecTests/client-side-encryption/external/external-schema.json +++ b/tests/SpecTests/client-side-encryption/external/external-schema.json @@ -5,12 +5,12 @@ "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } } diff --git a/tests/SpecTests/client-side-encryption/limits/limits-doc.json b/tests/SpecTests/client-side-encryption/limits/limits-doc.json index 3c556aee0..53de52326 100644 --- a/tests/SpecTests/client-side-encryption/limits/limits-doc.json +++ b/tests/SpecTests/client-side-encryption/limits/limits-doc.json @@ -1,102 +1,102 @@ { - "00": "a", - "01": "a", - "02": "a", - "03": "a", - "04": "a", - "05": "a", - "06": "a", - "07": "a", - "08": "a", - "09": "a", - "10": "a", - "11": "a", - "12": "a", - "13": "a", - "14": "a", - "15": "a", - "16": "a", - "17": "a", - "18": "a", - "19": "a", - "20": "a", - "21": "a", - "22": "a", - "23": "a", - "24": "a", - "25": "a", - "26": "a", - "27": "a", - "28": "a", - "29": "a", - "30": "a", - "31": "a", - "32": "a", - "33": "a", - "34": "a", - "35": "a", - "36": "a", - "37": "a", - "38": "a", - "39": "a", - "40": "a", - "41": "a", - "42": "a", - "43": "a", - "44": "a", - "45": "a", - "46": "a", - "47": "a", - "48": "a", - "49": "a", - "50": "a", - "51": "a", - "52": "a", - "53": "a", - "54": "a", - "55": "a", - "56": "a", - "57": "a", - "58": "a", - "59": "a", - "60": "a", - "61": "a", - "62": "a", - "63": "a", - "64": "a", - "65": "a", - "66": "a", - "67": "a", - "68": "a", - "69": "a", - "70": "a", - "71": "a", - "72": "a", - "73": "a", - "74": "a", - "75": "a", - "76": "a", - "77": "a", - "78": "a", - "79": "a", - "80": "a", - "81": "a", - "82": "a", - "83": "a", - "84": "a", - "85": "a", - "86": "a", - "87": "a", - "88": "a", - "89": "a", - "90": "a", - "91": "a", - "92": "a", - "93": "a", - "94": "a", - "95": "a", - "96": "a", - "97": "a", - "98": "a", + "00": "a", + "01": "a", + "02": "a", + "03": "a", + "04": "a", + "05": "a", + "06": "a", + "07": "a", + "08": "a", + "09": "a", + "10": "a", + "11": "a", + "12": "a", + "13": "a", + "14": "a", + "15": "a", + "16": "a", + "17": "a", + "18": "a", + "19": "a", + "20": "a", + "21": "a", + "22": "a", + "23": "a", + "24": "a", + "25": "a", + "26": "a", + "27": "a", + "28": "a", + "29": "a", + "30": "a", + "31": "a", + "32": "a", + "33": "a", + "34": "a", + "35": "a", + "36": "a", + "37": "a", + "38": "a", + "39": "a", + "40": "a", + "41": "a", + "42": "a", + "43": "a", + "44": "a", + "45": "a", + "46": "a", + "47": "a", + "48": "a", + "49": "a", + "50": "a", + "51": "a", + "52": "a", + "53": "a", + "54": "a", + "55": "a", + "56": "a", + "57": "a", + "58": "a", + "59": "a", + "60": "a", + "61": "a", + "62": "a", + "63": "a", + "64": "a", + "65": "a", + "66": "a", + "67": "a", + "68": "a", + "69": "a", + "70": "a", + "71": "a", + "72": "a", + "73": "a", + "74": "a", + "75": "a", + "76": "a", + "77": "a", + "78": "a", + "79": "a", + "80": "a", + "81": "a", + "82": "a", + "83": "a", + "84": "a", + "85": "a", + "86": "a", + "87": "a", + "88": "a", + "89": "a", + "90": "a", + "91": "a", + "92": "a", + "93": "a", + "94": "a", + "95": "a", + "96": "a", + "97": "a", + "98": "a", "99": "a" } \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/limits/limits-key.json b/tests/SpecTests/client-side-encryption/limits/limits-key.json index 5da84ab6b..b3fe0723b 100644 --- a/tests/SpecTests/client-side-encryption/limits/limits-key.json +++ b/tests/SpecTests/client-side-encryption/limits/limits-key.json @@ -1,27 +1,27 @@ { "status": { "$numberInt": "1" - }, + }, "_id": { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } - }, + }, "masterKey": { "provider": "local" - }, + }, "updateDate": { "$date": { "$numberLong": "1557827033449" } - }, + }, "keyMaterial": { "$binary": { - "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", + "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", "subType": "00" } - }, + }, "creationDate": { "$date": { "$numberLong": "1557827033449" diff --git a/tests/SpecTests/client-side-encryption/limits/limits-schema.json b/tests/SpecTests/client-side-encryption/limits/limits-schema.json index 7af0979b8..c06908d9c 100644 --- a/tests/SpecTests/client-side-encryption/limits/limits-schema.json +++ b/tests/SpecTests/client-side-encryption/limits/limits-schema.json @@ -5,1401 +5,1401 @@ "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "01": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "02": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "03": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "04": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "05": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "06": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "07": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "08": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "09": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "10": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "11": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "12": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "13": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "14": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "15": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "16": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "17": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "18": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "19": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "20": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "21": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "22": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "23": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "24": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "25": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "26": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "27": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "28": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "29": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "30": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "31": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "32": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "33": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "34": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "35": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "36": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "37": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "38": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "39": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "40": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "41": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "42": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "43": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "44": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "45": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "46": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "47": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "48": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "49": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "50": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "51": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "52": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "53": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "54": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "55": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "56": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "57": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "58": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "59": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "60": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "61": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "62": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "63": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "64": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "65": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "66": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "67": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "68": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "69": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "70": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "71": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "72": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "73": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "74": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "75": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "76": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "77": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "78": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "79": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "80": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "81": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "82": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "83": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "84": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "85": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "86": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "87": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "88": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "89": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "90": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "91": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "92": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "93": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "94": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "95": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "96": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "97": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "98": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } - }, + }, "99": { "encrypt": { "keyId": [ { "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } - ], - "bsonType": "string", + ], + "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } } - }, + }, "bsonType": "object" } \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/tests/aggregate.json b/tests/SpecTests/client-side-encryption/tests/aggregate.json index a9e79f9ed..7de725b71 100644 --- a/tests/SpecTests/client-side-encryption/tests/aggregate.json +++ b/tests/SpecTests/client-side-encryption/tests/aggregate.json @@ -150,18 +150,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -273,18 +261,6 @@ "command_name": "aggregate" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/awsTemporary.json b/tests/SpecTests/client-side-encryption/tests/awsTemporary.json new file mode 100644 index 000000000..10eb85fee --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/awsTemporary.json @@ -0,0 +1,225 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": { + "encrypted_w_altname": { + "encrypt": { + "keyId": "/altname", + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "random": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string_equivalent": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "status": 1, + "_id": { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "keyAltNames": [ + "altname", + "another_altname" + ] + } + ], + "tests": [ + { + "description": "Insert a document with auto encryption using the AWS provider with temporary credentials", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "awsTemporary": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "Insert with invalid temporary credentials", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "awsTemporaryNoSessionToken": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "result": { + "errorContains": "security token" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/azureKMS.json b/tests/SpecTests/client-side-encryption/tests/azureKMS.json new file mode 100644 index 000000000..afecf40b0 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/azureKMS.json @@ -0,0 +1,224 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": { + "encrypted_string_aws": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_azure": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_gcp": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_local": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_kmip": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "dBHpr8aITfeBQ15grpbLpQ==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "n+HWZ0ZSVOYA3cvQgP7inN4JSXfOH85IngmeQxRpQHjCCcqT3IFqEWNlrsVHiz3AELimHhX4HKqOLWMUeSIT6emUDDoQX9BAv8DR1+E1w4nGs/NyEneac78EYFkK3JysrFDOgl2ypCCTKAypkn9CkAx1if4cfgQE93LW4kczcyHdGiH36CIxrCDGv1UzAvERN5Qa47DVwsM6a+hWsF2AAAJVnF0wYLLJU07TuRHdMrrphPWXZsFgyV+lRqJ7DDpReKNO8nMPLV/mHqHBHGPGQiRdb9NoJo8CvokGz4+KE8oLwzKf6V24dtwZmRkrsDV4iOhvROAzz+Euo1ypSkL3mw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1601573901680" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1601573901680" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyAltNames": [ + "altname", + "azure_altname" + ] + } + ], + "tests": [ + { + "description": "Insert a document with auto encryption using Azure KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "azure": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_azure": "string0" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_azure": { + "$binary": { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string_azure": { + "$binary": { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/badQueries.json b/tests/SpecTests/client-side-encryption/tests/badQueries.json index 824a53c00..4968307ba 100644 --- a/tests/SpecTests/client-side-encryption/tests/badQueries.json +++ b/tests/SpecTests/client-side-encryption/tests/badQueries.json @@ -1318,7 +1318,7 @@ } }, "result": { - "errorContains": "Cannot encrypt element of type array" + "errorContains": "Cannot encrypt element of type" } } ] @@ -1387,7 +1387,7 @@ } }, "result": { - "errorContains": "Cannot encrypt element of type array" + "errorContains": "Cannot encrypt element of type" } } ] diff --git a/tests/SpecTests/client-side-encryption/tests/basic.json b/tests/SpecTests/client-side-encryption/tests/basic.json index 3f9895fd5..3ed066f53 100644 --- a/tests/SpecTests/client-side-encryption/tests/basic.json +++ b/tests/SpecTests/client-side-encryption/tests/basic.json @@ -144,18 +144,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -283,18 +271,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/bulk.json b/tests/SpecTests/client-side-encryption/tests/bulk.json index ead90985a..1b62e5e8a 100644 --- a/tests/SpecTests/client-side-encryption/tests/bulk.json +++ b/tests/SpecTests/client-side-encryption/tests/bulk.json @@ -178,18 +178,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/bypassedCommand.json b/tests/SpecTests/client-side-encryption/tests/bypassedCommand.json index bd0b1c565..18054a70c 100644 --- a/tests/SpecTests/client-side-encryption/tests/bypassedCommand.json +++ b/tests/SpecTests/client-side-encryption/tests/bypassedCommand.json @@ -78,7 +78,7 @@ ] }, { - "description": "current op is not bypassed", + "description": "kill op is not bypassed", "clientOptions": { "autoEncryptOpts": { "kmsProviders": { @@ -90,14 +90,15 @@ { "name": "runCommand", "object": "database", - "command_name": "currentOp", + "command_name": "killOp", "arguments": { "command": { - "currentOp": 1 + "killOp": 1, + "op": 1234 } }, "result": { - "errorContains": "command not supported for auto encryption: currentOp" + "errorContains": "command not supported for auto encryption: killOp" } } ] diff --git a/tests/SpecTests/client-side-encryption/tests/count.json b/tests/SpecTests/client-side-encryption/tests/count.json index 24f46a110..9df8cd639 100644 --- a/tests/SpecTests/client-side-encryption/tests/count.json +++ b/tests/SpecTests/client-side-encryption/tests/count.json @@ -149,18 +149,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/countDocuments.json b/tests/SpecTests/client-side-encryption/tests/countDocuments.json index 3cf5fbca8..07ff97f26 100644 --- a/tests/SpecTests/client-side-encryption/tests/countDocuments.json +++ b/tests/SpecTests/client-side-encryption/tests/countDocuments.json @@ -150,18 +150,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/create-and-createIndexes.json b/tests/SpecTests/client-side-encryption/tests/create-and-createIndexes.json new file mode 100644 index 000000000..48638a97c --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/create-and-createIndexes.json @@ -0,0 +1,115 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "tests": [ + { + "description": "create is OK", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "unencryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "unencryptedCollection", + "validator": { + "unencrypted_string": "foo" + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "unencryptedCollection" + } + } + ] + }, + { + "description": "createIndexes is OK", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "unencryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "unencryptedCollection" + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "createIndexes": "unencryptedCollection", + "indexes": [ + { + "name": "name", + "key": { + "name": 1 + } + } + ] + } + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "unencryptedCollection", + "index": "name" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/delete.json b/tests/SpecTests/client-side-encryption/tests/delete.json index 30fb453a9..a6f4ffde9 100644 --- a/tests/SpecTests/client-side-encryption/tests/delete.json +++ b/tests/SpecTests/client-side-encryption/tests/delete.json @@ -151,18 +151,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -276,18 +264,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/distinct.json b/tests/SpecTests/client-side-encryption/tests/distinct.json index 7a5f75c4a..9786b0781 100644 --- a/tests/SpecTests/client-side-encryption/tests/distinct.json +++ b/tests/SpecTests/client-side-encryption/tests/distinct.json @@ -161,18 +161,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/explain.json b/tests/SpecTests/client-side-encryption/tests/explain.json index 5ad46bc23..0e451e481 100644 --- a/tests/SpecTests/client-side-encryption/tests/explain.json +++ b/tests/SpecTests/client-side-encryption/tests/explain.json @@ -155,18 +155,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/find.json b/tests/SpecTests/client-side-encryption/tests/find.json index b7c5258a1..1feddab0e 100644 --- a/tests/SpecTests/client-side-encryption/tests/find.json +++ b/tests/SpecTests/client-side-encryption/tests/find.json @@ -160,18 +160,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -302,18 +290,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/findOneAndDelete.json b/tests/SpecTests/client-side-encryption/tests/findOneAndDelete.json index 6261d8601..e418a4581 100644 --- a/tests/SpecTests/client-side-encryption/tests/findOneAndDelete.json +++ b/tests/SpecTests/client-side-encryption/tests/findOneAndDelete.json @@ -148,18 +148,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/findOneAndReplace.json b/tests/SpecTests/client-side-encryption/tests/findOneAndReplace.json index d91bc0599..78baca843 100644 --- a/tests/SpecTests/client-side-encryption/tests/findOneAndReplace.json +++ b/tests/SpecTests/client-side-encryption/tests/findOneAndReplace.json @@ -147,18 +147,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/findOneAndUpdate.json b/tests/SpecTests/client-side-encryption/tests/findOneAndUpdate.json index fad70609a..1d8585115 100644 --- a/tests/SpecTests/client-side-encryption/tests/findOneAndUpdate.json +++ b/tests/SpecTests/client-side-encryption/tests/findOneAndUpdate.json @@ -149,18 +149,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-BypassQueryAnalysis.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-BypassQueryAnalysis.json new file mode 100644 index 000000000..9b28df2f9 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-BypassQueryAnalysis.json @@ -0,0 +1,261 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "BypassQueryAnalysis decrypts", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "bypassQueryAnalysis": true + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": { + "$binary": { + "base64": "C18BAAAFZAAgAAAAANnt+eLTkv4GdDPl8IAfJOvTzArOgFJQ2S/DcLza4W0DBXMAIAAAAAD2u+omZme3P2gBPehMQyQHQ153tPN1+z7bksYA9jKTpAVwADAAAAAAUnCOQqIvmR65YKyYnsiVfVrg9hwUVO3RhhKExo3RWOzgaS0QdsBL5xKFS0JhZSoWBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAEjRWeBI0mHYSNBI0VniQEpQbp/ZJpWBKeDtKLiXb0P2E9wvc0g3f373jnYQYlJquOrlPOoEy3ngsHPJuSUijvWDsrQzqYa349K7G/66qaXEFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAACkm0o9bj6j0HuADKc0svbqO2UHj6GrlNdF6yKNxh63xRJrAAAAAAAAAAAAAA==", + "subType": "06" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_id": 1, + "encryptedIndexed": "123" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedIndexed": { + "$binary": { + "base64": "C18BAAAFZAAgAAAAANnt+eLTkv4GdDPl8IAfJOvTzArOgFJQ2S/DcLza4W0DBXMAIAAAAAD2u+omZme3P2gBPehMQyQHQ153tPN1+z7bksYA9jKTpAVwADAAAAAAUnCOQqIvmR65YKyYnsiVfVrg9hwUVO3RhhKExo3RWOzgaS0QdsBL5xKFS0JhZSoWBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAEjRWeBI0mHYSNBI0VniQEpQbp/ZJpWBKeDtKLiXb0P2E9wvc0g3f373jnYQYlJquOrlPOoEy3ngsHPJuSUijvWDsrQzqYa349K7G/66qaXEFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAACkm0o9bj6j0HuADKc0svbqO2UHj6GrlNdF6yKNxh63xRJrAAAAAAAAAAAAAA==", + "subType": "06" + } + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Compact.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Compact.json new file mode 100644 index 000000000..27310cb59 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Compact.json @@ -0,0 +1,230 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + }, + { + "_id": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "HBk9BWihXExNDvTp1lUxOuxuZK2Pe2ZdVdlsxPEBkiO1bS4mG5NNDsQ7zVxJAH8BtdOYp72Ku4Y3nwc0BUpIKsvAKX4eYXtlhv5zUQxWdeNFhg9qK7qb8nqhnnLeT0f25jFSqzWJoT379hfwDeu0bebJHr35QrJ8myZdPMTEDYF08QYQ48ShRBli0S+QzBHHAQiM2iJNr4svg2WR8JSeWQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Compact works", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "runCommand", + "object": "database", + "command_name": "compactStructuredEncryptionData", + "arguments": { + "command": { + "compactStructuredEncryptionData": "default" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "compactStructuredEncryptionData": "default", + "compactionTokens": { + "encryptedIndexed": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + }, + "encryptedUnindexed": { + "$binary": { + "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=", + "subType": "00" + } + } + } + }, + "command_name": "compactStructuredEncryptionData" + } + } + ] + }, + { + "description": "Compact errors on an unencrypted client", + "operations": [ + { + "name": "runCommand", + "object": "database", + "command_name": "compactStructuredEncryptionData", + "arguments": { + "command": { + "compactStructuredEncryptionData": "default" + } + }, + "result": { + "errorContains": "'compactStructuredEncryptionData.compactionTokens' is missing" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-CreateCollection-OldServer.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-CreateCollection-OldServer.json new file mode 100644 index 000000000..c266aa6b8 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-CreateCollection-OldServer.json @@ -0,0 +1,94 @@ +{ + "runOn": [ + { + "minServerVersion": "6.0.0", + "maxServerVersion": "6.3.99", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "tests": [ + { + "description": "driver returns an error if creating a QEv2 collection on unsupported server", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + }, + "result": { + "errorContains": "Driver support of Queryable Encryption is incompatible with server. Upgrade server to use Queryable Encryption." + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-CreateCollection.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-CreateCollection.json new file mode 100644 index 000000000..c324be8ab --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-CreateCollection.json @@ -0,0 +1,1758 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "tests": [ + { + "description": "state collections and index are created", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection", + "index": "__safeContent___1" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "encryptedCollection" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "command_name": "createIndexes", + "database_name": "default" + } + } + ] + }, + { + "description": "default state collection names are applied", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection", + "index": "__safeContent___1" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "encryptedCollection" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "command_name": "createIndexes", + "database_name": "default" + } + } + ] + }, + { + "description": "drop removes all state collections", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection", + "index": "__safeContent___1" + } + }, + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + }, + { + "name": "assertIndexNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection", + "index": "__safeContent___1" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "encryptedCollection" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "command_name": "createIndexes", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "encryptedCollection" + }, + "command_name": "drop", + "database_name": "default" + } + } + ] + }, + { + "description": "CreateCollection without encryptedFields.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "plaintextCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "plaintextCollection" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "plaintextCollection" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "plaintextCollection" + } + }, + "command_name": "listCollections", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "plaintextCollection" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "plaintextCollection" + }, + "command_name": "create", + "database_name": "default" + } + } + ] + }, + { + "description": "CreateCollection from encryptedFieldsMap.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection", + "index": "__safeContent___1" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "encryptedCollection" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "command_name": "createIndexes", + "database_name": "default" + } + } + ] + }, + { + "description": "CreateCollection from encryptedFields.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection", + "index": "__safeContent___1" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "encryptedCollection" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "encryptedCollection" + } + }, + "command_name": "listCollections", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "command_name": "createIndexes", + "database_name": "default" + } + } + ] + }, + { + "description": "DropCollection from encryptedFieldsMap", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "encryptedCollection" + }, + "command_name": "drop", + "database_name": "default" + } + } + ] + }, + { + "description": "DropCollection from encryptedFields", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "encryptedFieldsMap": {} + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection", + "index": "__safeContent___1" + } + }, + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "encryptedCollection" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "encryptedCollection" + } + }, + "command_name": "listCollections", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "command_name": "createIndexes", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "encryptedCollection" + }, + "command_name": "drop", + "database_name": "default" + } + } + ] + }, + { + "description": "DropCollection from remote encryptedFields", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "encryptedFieldsMap": {} + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection", + "index": "__safeContent___1" + } + }, + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "encryptedCollection" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "command_name": "create", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "encryptedCollection" + } + }, + "command_name": "listCollections", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "command_name": "createIndexes", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "encryptedCollection" + } + }, + "command_name": "listCollections", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "command_name": "drop", + "database_name": "default" + } + }, + { + "command_started_event": { + "command": { + "drop": "encryptedCollection" + }, + "command_name": "drop", + "database_name": "default" + } + } + ] + }, + { + "description": "encryptedFields are consulted for metadata collection names", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "escCollection": "invalid_esc_name", + "ecocCollection": "invalid_ecoc_name", + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + }, + "result": { + "errorContains": "Encrypted State Collection name should follow" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-DecryptExistingData.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-DecryptExistingData.json new file mode 100644 index 000000000..1fb4c1d1b --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-DecryptExistingData.json @@ -0,0 +1,149 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + ], + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "HBk9BWihXExNDvTp1lUxOuxuZK2Pe2ZdVdlsxPEBkiO1bS4mG5NNDsQ7zVxJAH8BtdOYp72Ku4Y3nwc0BUpIKsvAKX4eYXtlhv5zUQxWdeNFhg9qK7qb8nqhnnLeT0f25jFSqzWJoT379hfwDeu0bebJHr35QrJ8myZdPMTEDYF08QYQ48ShRBli0S+QzBHHAQiM2iJNr4svg2WR8JSeWQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 decrypt of existing data succeeds", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_id": 1, + "encryptedUnindexed": "value123" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Delete.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Delete.json new file mode 100644 index 000000000..ddfe57b00 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Delete.json @@ -0,0 +1,284 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Delete can query an FLE2 indexed field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "value123" + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedIndexed": "value123" + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.json new file mode 100644 index 000000000..bdc5c99bc --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.json @@ -0,0 +1,212 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "HBk9BWihXExNDvTp1lUxOuxuZK2Pe2ZdVdlsxPEBkiO1bS4mG5NNDsQ7zVxJAH8BtdOYp72Ku4Y3nwc0BUpIKsvAKX4eYXtlhv5zUQxWdeNFhg9qK7qb8nqhnnLeT0f25jFSqzWJoT379hfwDeu0bebJHr35QrJ8myZdPMTEDYF08QYQ48ShRBli0S+QzBHHAQiM2iJNr4svg2WR8JSeWQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "encryptedFieldsMap is preferred over remote encryptedFields", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "encryptedFieldsMap": { + "default.default": { + "fields": [] + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_id": 1, + "encryptedUnindexed": "value123" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-EncryptedFields-vs-jsonSchema.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-EncryptedFields-vs-jsonSchema.json new file mode 100644 index 000000000..8e0c6dafa --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-EncryptedFields-vs-jsonSchema.json @@ -0,0 +1,300 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": {}, + "bsonType": "object" + }, + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "encryptedFields is preferred over jsonSchema", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "123" + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedIndexed": "123" + } + }, + "result": [ + { + "_id": 1, + "encryptedIndexed": "123" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVsACAAAAAApJtKPW4+o9B7gAynNLL26jtlB4+hq5TXResijcYet8USY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-EncryptedFieldsMap-defaults.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-EncryptedFieldsMap-defaults.json new file mode 100644 index 000000000..1c0a057ca --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-EncryptedFieldsMap-defaults.json @@ -0,0 +1,105 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "key_vault_data": [], + "tests": [ + { + "description": "default state collections are applied to encryptionInformation", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "encryptedFieldsMap": { + "default.default": { + "fields": [] + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "foo": { + "$binary": { + "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "foo": { + "$binary": { + "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + ], + "encryptionInformation": { + "type": { + "$numberInt": "1" + }, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [] + } + } + }, + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "foo": { + "$binary": { + "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-FindOneAndUpdate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-FindOneAndUpdate.json new file mode 100644 index 000000000..c5e689a3d --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-FindOneAndUpdate.json @@ -0,0 +1,560 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "findOneAndUpdate can query an FLE2 indexed field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "value123" + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedIndexed": "value123" + }, + "update": { + "$set": { + "foo": "bar" + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedIndexed": "value123" + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "foo": "bar" + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "foo": "bar", + "__safeContent__": [ + { + "$binary": { + "base64": "ThpoKfQ8AkOzkFfNC1+9PF0pY2nIzfXvRdxQgjkNbBw=", + "subType": "00" + } + } + ] + } + ] + } + } + }, + { + "description": "findOneAndUpdate can modify an FLE2 indexed field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "value123" + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedIndexed": "value123" + }, + "update": { + "$set": { + "encryptedIndexed": "value456" + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedIndexed": "value123" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "encryptedIndexed": "value456" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedIndexed": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": { + "$eq": 1 + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "rhe7/w8Ob8Unl44rGr/moScx6m5VODQnscDhF4Nkn6g=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-InsertFind-Indexed.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-InsertFind-Indexed.json new file mode 100644 index 000000000..6e156ffc6 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-InsertFind-Indexed.json @@ -0,0 +1,296 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Insert and find FLE2 indexed field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "123" + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedIndexed": "123" + } + }, + "result": [ + { + "_id": 1, + "encryptedIndexed": "123" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVsACAAAAAApJtKPW4+o9B7gAynNLL26jtlB4+hq5TXResijcYet8USY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-InsertFind-Unindexed.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-InsertFind-Unindexed.json new file mode 100644 index 000000000..48280f5bd --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-InsertFind-Unindexed.json @@ -0,0 +1,248 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "HBk9BWihXExNDvTp1lUxOuxuZK2Pe2ZdVdlsxPEBkiO1bS4mG5NNDsQ7zVxJAH8BtdOYp72Ku4Y3nwc0BUpIKsvAKX4eYXtlhv5zUQxWdeNFhg9qK7qb8nqhnnLeT0f25jFSqzWJoT379hfwDeu0bebJHr35QrJ8myZdPMTEDYF08QYQ48ShRBli0S+QzBHHAQiM2iJNr4svg2WR8JSeWQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Insert and find FLE2 unindexed field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedUnindexed": "value123" + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_id": 1, + "encryptedUnindexed": "value123" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedUnindexed": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": { + "$eq": 1 + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encryptedUnindexed": { + "$$type": "binData" + } + } + ] + } + } + }, + { + "description": "Query with an unindexed field fails", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedUnindexed": "value123" + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedUnindexed": "value123" + } + }, + "result": { + "errorContains": "encrypt" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-MissingKey.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-MissingKey.json new file mode 100644 index 000000000..8812a1f0a --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-MissingKey.json @@ -0,0 +1,116 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + ], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + }, + "key_vault_data": [], + "tests": [ + { + "description": "FLE2 encrypt fails with mising key", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "123" + } + }, + "result": { + "errorContains": "not all keys requested were satisfied" + } + } + ] + }, + { + "description": "FLE2 decrypt fails with mising key", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": {} + }, + "result": { + "errorContains": "not all keys requested were satisfied" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-NoEncryption.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-NoEncryption.json new file mode 100644 index 000000000..a6843c473 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-NoEncryption.json @@ -0,0 +1,87 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "key_vault_data": [], + "encrypted_fields": { + "fields": [] + }, + "tests": [ + { + "description": "insert with no encryption succeeds", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "foo": "bar" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "foo": "bar" + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "foo": "bar" + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Aggregate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Aggregate.json new file mode 100644 index 000000000..ba53b007b --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Aggregate.json @@ -0,0 +1,508 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Date. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Correctness.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Correctness.json new file mode 100644 index 000000000..e9620efbe --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Correctness.json @@ -0,0 +1,1839 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$lte": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "200" + } + } + } + } + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + }, + "$lt": { + "$date": { + "$numberLong": "2" + } + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + }, + "$lte": { + "$date": { + "$numberLong": "200" + } + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$in": [ + { + "$date": { + "$numberLong": "0" + } + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "-1" + } + } + } + }, + "result": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedDate": { + "$date": { + "$numberLong": "200" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 200, + "encryptedDate": { + "$date": { + "$numberLong": "200" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "1" + } + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "1" + } + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$lte": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "0" + } + } + } + } + } + ] + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "200" + } + } + } + } + } + ] + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + }, + "$lt": { + "$date": { + "$numberLong": "2" + } + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + }, + "$lte": { + "$date": { + "$numberLong": "200" + } + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$in": [ + { + "$date": { + "$numberLong": "0" + } + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$numberDouble": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gte": { + "$numberDouble": "0" + } + } + } + }, + "result": { + "errorContains": "value type is a date" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Delete.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Delete.json new file mode 100644 index 000000000..daaa09389 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Delete.json @@ -0,0 +1,436 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Date. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-FindOneAndUpdate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-FindOneAndUpdate.json new file mode 100644 index 000000000..8500fa829 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-FindOneAndUpdate.json @@ -0,0 +1,514 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Date. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "update": { + "$set": { + "encryptedDate": { + "$date": { + "$numberLong": "2" + } + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedDate": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-InsertFind.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-InsertFind.json new file mode 100644 index 000000000..7de45ba00 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-InsertFind.json @@ -0,0 +1,499 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Date. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Update.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Update.json new file mode 100644 index 000000000..d5b62be06 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Date-Update.json @@ -0,0 +1,516 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Date. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "update": { + "$set": { + "encryptedDate": { + "$date": { + "$numberLong": "2" + } + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDate": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Aggregate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Aggregate.json new file mode 100644 index 000000000..081bc577f --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Aggregate.json @@ -0,0 +1,1902 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Decimal. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RGTjNVEsNJb+DG7DpPOam8rQWD5HZAMpRyiTQaw7tk8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RlQWwhU+uVv0a+9IB5cUkEfvHBvOw3B1Sx6WfPWMqes=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubb81XTC7U+4tcNzf1oYvOY6gR5hC2Izqx54f4GuJ0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6M4Q5NMQ9TqNnjzGOxIkiUIY8TEL0I3XD1QnhefQUqU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BtInzk9t2FFMCEY6AQ7zN8jwrrZEs2irSv6q0Q4NaIw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vxXfETu9cuBIpRBo3jUUU04mJIH/aAhLX8K6VI5Xv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXPCdS+q23zi1bkPnaVG2j0PsVtxdeSLJ//h6J1x8RU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KY3KkfBAsN2l80wbpj41G0gwBR5KmmFnZcagg7D3ENk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI8NFAxXCX4VOnY5X73K6KI/Yspd3aR94KV39MhJlAw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nFxH0UC3mATKA6Vboz+QX/hAjj19kF/SH6H5Cne7qC0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q8hYqIYaIi7nOdG/7qQZYnz8Bsacfi66M1nVku4SH08=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4saA92R4arp4anvD9xFtze+sNcQqTEhPHyl1h70A8NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DbIziOBRRyeQS6RtBR09E37LV+CTKrEjGoRMLSpG6eE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Fv80Plp/7w2gnVqrwawLd6qhJ10G4NCDm3re67cNq4Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T/T2oiQCBBES4YN7EodzPRdabZSFlYIClHBym+bQUZE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQgHD3l46Ujqtbnj1VbbeM29C9wJzOhz+yZ/7XdSrxk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ltlFKzWvyZvHxDFOYDd/XXJ6kUiJj0ln2HTCEz2o4Z4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "flW8A7bltC1u8bzx0WJtxosGJdOVsJFfbx33jxnpFGg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SXO+92QbMKwUSG2t27ciunV1c3VvFkUuDmSczpRe008=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+KioGs1GM+xRBzFE67ePTWj04KMSE5/Y6qUF7nJ5kvU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L3xNVbh6YH+RzqABN+5Jgb7T234Efpn766DmUvxIxgg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hPF+60mBYPjh21dEmPlBhKgyc9S2qLtTkypYvnqP2Fc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EletRsETy2HcjaPIm2c8CkT7ch/P3pJJDC8hasepcSU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "r5bMXUaNKqLPxZ+TG9HYTG4aSDgcpim27rN8rQFkM0w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Q7Erdr8+/S0wUEDDIqlS5XjBVWvhZY65K0uUDb6+Ns=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xEcnhXy35hbXNVBPOOt3TUHbxvKfQ48KjA9b6/rbMqQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T8bEpiQNgsEudXvyKE9SZlSvbpV/LUaslsdqgSFltyo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hIoiaF2YjnxDbODfhFEB+JGZ5nf8suD3Shck5bwQ3N0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qnA6qzejeRJ0rsZaZ0zOvKAaXyxt5lpscKQNYFZNl4k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "anAKCL2DN/le2VaP0n2ucYSEH/DaaEH/8Sa4OqTZsRA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JCZlBJaFm618oWYSnT9Jr1MtwFVw4BZjOzO+5yWgR90=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yxyk4n9762WzcDVGnTn4jCqUnSMIVCrLDIjCX1QVj34=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fDI6fdKvDJwim5/CQwWZEzcrXE3LHgy7FTtffcC7tXE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Vex+gcz5T+WkzsVZQrkqUR2ryyZbnaOGuWpYvjN0zCw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8TLEXz+Gbbp6llHpZXVjLsdlYY9f6hrKpHVpyfDe0RY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fTyt5BrunypS65TfOzFW2E2qdIuT4SLeDeGlbQoJCs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8fKGrkqN0/KuSjyXgDBmRauDKrSa//JBKRWHEB9xBf4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s4codmG7uN4ss6P357jL21lazEe90M9GOK5WrOknSV0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RkSpua8XF+NUdxVDU90EbLUTTyZFX3tt3atBTroFaRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LnTCuCDyAHK5B9KXzjtwGmWB+qergQk2OCjnIx9MI2A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cBFh0virAX4pVXf/udIGI2951i0+0aZAdJcBVGtYnT4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "G54X6myQXWZ5fw/G31en3QbdgfXzL9+hFTtJpnWMqDI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EdsiiuezcsFJFnYIyGjCOhnqMj1BOwTB5EFxN+ERUkg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dVH9MXLtk0WTwGQ3xmrhOqfropMUkDW3o6paNPGl3NU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sB3HqXKWY3pKbuEH8BTbfNIGfbY+7/ZbOc3XC+JRNNI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WHyDk62Xhqbo4/iie2aLIM4x2uuAjv6102dJSHI58oM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pNUFuHpeNRDUZ/NrtII2c6sNc9eGR1lIUlIyXKERA+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UPa+pdCqnN0bfAptdzldQOSd01gidrDKy8KhWrpSKAI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l+7dOAlo+HUffMqFYXL6pgUFeTbwOM9CjKQLxEoLtc4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SRnDXV/rN6C8xwMutv9E1luv3DOUio3VkgPr8Cpm7Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QcH6gl+gX7xZ7OWhUNQMbndJy0Piz49pDo6RsnLkVSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "t+uL4DnfsI/Zll/KXWW1cOKX3Hu8WIkm3pt9efCVSAQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "myutHDctku/+Uug/nD8gRbYvmx/IovtoAAC2/fz2oHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6C+cjD0e0nSCP6cPqQYbNG7SlOd6Mfvi8hyfm7Ng+D8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zg01JSoOj9oBKT0S1ldJucXzY5AKgreS+h2xJreWTOs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7qQ80/FjodHl1m1py/Oii0/9C/xWbLdhaRXQ+kkCP10=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YwWMNH07vL6c5Nhg+MRnVByhzUunu8y0VLM9z/XvR5U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dle8bU98+fudAbc14SToZFkwvV3tcYVsjDug0NWljpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "J+eKL1vPJmlzltvhI6Li5Fz/TJmi3Ng+ehRTcs46API=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB3XzfFygLwC3WHkj0up+VbEd25KKoce1vOpG/5bwK4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vnVnmOnL+z2pqwE+A6cVKS0Iwy4F4/2IiElJca9bUQM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+lG5r/Fpqry3BtFuvY67+RntmHAMDoLVOSGc6ZoXPb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L5MXQertqc6uj7ADe8aWKbd1sYHPCE7P1VYVg9Zc3VI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "imKONuZgopt0bhM3GMX2WVPwQYMTobuUUEdhcLfHs4c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eOkU1J1uVbiVFWBerbXsSIVcF2nqiicTkFy4x7kFHB8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gI0uDhXeoH/UatDQKEf4qo8FHzWZDhb/wuWTqbq/ID4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cOkd5Aa3btYhtojE/smsF/PJnULqQ4NNqTkU6KXTFmo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AWNJMs1MTe294oFipp8Y6P0CjpkZ4qCZoClQF3XcHq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6gJtlzXOFhGYrVbTuRMmvMlDTwXdNtR9aGBlHZPwIMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LEmwVGA/xsEG7UrcOoYLFu6KCXgijzFznenknuDacm8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mIRFPTXRrGaPtp/Ydij2jgkRe4uoUvAKxW2d8b9zYL0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+Uv2u48WALOO0L311z+eryjYQzKJVMfdHMZPhOAFmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "INXXp0wDyVCq+NtfIrrC2ciETmyW/dWB/48/u4yLEZ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "se7DGo8XrlrQDLEcco1tZrQt9kDe+0RTyl2bw/quG4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vr0m2+Zk9lbN6UgWCyn8xJWJOokU3IDYab5U5q1+CgQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XI+eJ8Gy2JktG1gICgoj1qpsfy1tKmH0kglWbaQH6DA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A+UCuNnuAUqnQzspA6TVqUPRmtZmpSex5HFw7THRxs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaH2Ehfljd19uo0Fvb3iwkdaiWEVQd2YPoitgEPkhSM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S/iZBJGcc8+qZxyMtab65MMBoSglybwk3x58Nb86gnY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w14ZE5qqY5YgkS4Zcs9YNbrQbY1XfGOOHNn9bOYnFVQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0MhGd/jEF1vjkKGp+ZMn9SjLK54jkp9W4Hg+Sp/oxaI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92QZ73e/NRTYgCm4aifaKth6aAsKnLLccBc0zx/qUTY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WOjzemCgFJOiGIp81RSVh/tFlzSTj9eFWcBnsiv2Ycs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DrsP9CmfKPjw5yLL8bnSeAxfNzAwlb+Z8OqCiKgBY7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lMogqg8veBv6mri3/drMe9afJiKMvevkmGcw9BedfLo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TxqwNcY8Tg2MPpNdkPBwvfpuTttSYRHU26DGECKYQ9o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l0u1b4b4vYACWIwfnB7PZac4oDEgjQZCzHruNPTgAIY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iVSGQ+cCfhbWIrY/v/WBORK92elu9gfRKyGhr6r/k00=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yK1forG50diEXte8ECzjfpHeYsPyuQ/dgxbxn/nzY5k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gIfTLCD3VwnOwkC0zPXWTqaITxX6ZplA69PO2a6zolc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O/Zxlgh3WqpzJ7+Sd8XWMVID4/GXJUUWaSqfgDUi3b0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQ6yv368zwahUqSUYH/StL0Qgz/TwS1CzlMjVDvCciI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m2rPEYkjwyiKdonMrKlcF7hya4lFOAUwEePJ3SgrNx8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mq0yl5iVKlq71bT/dT/fXOWf2n90bTnXFnOdGDN0JOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6qDGMXipPLC2O6EAAMjO2F9xx4rdqZso4IkPpH2304U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jvQHRQQa2RIszE2LX2Hv2LbRhYawJ6qmtRt8HZzFQXg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ovJXQrkZlpeHRciKyE/WWNm5O389gRgzx1W+Dw596X4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "a4kgRNvYctGYqyQv9qScL/WkljTYVylJ9pE9KDULlxU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qV4Q48vPiCJMTjljotzYKI/zfExWpkKOSHGcAjGyDig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jtI7zbBF+QW/aYYTkn90zzyHLXLgmy7l1bzgMb2oqic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q0KmJl9txPdn962UNvnfe6UFhdk9YaFZuTm33F+csso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ULNdEqeZJgtmNOhN/Y9INzsE9AnxWYwOMn+pIbRXIFs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "R4oz9+wkdjpKe5tE1jpG7IURAnfvS5fLP4LrD5cZfTE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qG5Z7VhwSu/HT/YFTgDzyAAzJKq51xPw2HeEV5btYC4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OM/1DmIIZ5Qyhtq8TGkHTBEMVKjAnKRZMRXYtTG8ctc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2R5vZbljLXnDFA99YfGuRB7pAdPJVKsT25zLNMC0fUk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OMbavF2EmdAz1fHkLV3ctFEUDfriKhoT2gidwHZ9z1o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MWT4Zrw3/vVvTYMa1Is5Pjr3wEwnBfnEAPPUAHKQhNU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tBkRPfG9yxfKocQx5pAJX0oEHKPL0Tgtr+0UYe09InE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lqxpnDR/H0YgH7RcfKoNoaaRhe1SIazIeMbQ1fu9y3Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "utT1UdR22PWOTrOkZauztX613lAplV4eh/ejTRb7ZSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S+Y2yFyKi/a6FXhih4yGo29X8I8OT6/zwEoX6NMKT4o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QSjVppg29x6oS5yBg8OFjrFt0tuTpWCuKxfIy0k8YnE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y3r6/Xsfvsl3HksXlVYkJgHUqpQGfICxg3x9f8Zw1qM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BSltHzEwDjFN4du9rDHAPvl22atlcTioEtt+gC5L1tk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0arGXjSN0006UnXbrWsGqhvBair569DeFDUME3Df3rA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s/DumaMad08S+PBUUcrS+v42K0z8HgcdiQtrFAEu2Qs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EzJ8Y8N0OQBTlnvrK82PdevDNZZO4E6CNgYVu8Cj6Ks=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VA4vr8jBPI5QdiPrULzzZjBMIUbG3V7Slg5zm0bFcKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YAOvEB2ZLtq9LQiFViBHWaxxWVVonC2rNYj9tN9s3L0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hgaHMo9aAGS+nBwvqnTjZO+YkiQPY1c1XcIYeaYKHyI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YvaoLt3ZpH0atB0tNzwMjpoxRYJXl0DqSjisMJiGVBE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EMmW6CptFsiLoPOi5/uAJQ2FmeLg6mCpuVLLrRWk7Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1jQsNMarSnarlYmXEuoFokeBMg/090qUD9wqo1Zn8Gs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hupXNKhRpJxpyDAAP1TgJ5JMZh9lhbMk6s7D7dMS3C8=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Correctness.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Correctness.json new file mode 100644 index 000000000..12fe7c8bc --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Correctness.json @@ -0,0 +1,1155 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gte": { + "$numberDecimal": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$lte": { + "$numberDecimal": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gte": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$lte": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberInt": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Delete.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Delete.json new file mode 100644 index 000000000..ac49d16a2 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Delete.json @@ -0,0 +1,1110 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Decimal. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-FindOneAndUpdate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-FindOneAndUpdate.json new file mode 100644 index 000000000..88a235078 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-FindOneAndUpdate.json @@ -0,0 +1,1906 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Decimal. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mr/laWHUijZT5VT3x2a7crb7wgd/UXOGz8jr8BVqBpM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VDCpBYsJIxTfcI6Zgf7FTmKMxUffQv+Ys8zt5dlK76I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zYDslUwOUVNwTYkETfjceH/PU3bac9X3UuQyYJ19qK0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rAOmHSz18Jx107xpbv9fYcPOmh/KPAqge0PAtuhIRnc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BFOB1OGVUen7VsOuS0g8Ti7oDsTt2Yj/k/7ta8YAdGM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fckE5SPs0GU+akDkUEM6mm0EtcV3WDE/sQsnTtodlk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mi9+aNjuwIvaMpSHENvKzKRAmX9cYguo2mXLvOoftHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K6TWn4VcWWkz/gkUkLmbtwkG7SNeABICmLDnoYJFlLU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z+2/cEtGU0Fq7QJFNGA/0y4aWAsw0ncG6X0LYRqwS3c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rrSIf+lgcNZFbbUkS9BmE045jRWBpcBJXHzfMVEFuzE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KlHL3Kyje1/LMIfgbCqw1SolxffJvvgsYBV5y77wxuA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hzJ1YBoETmYeCh352dBmG8d8Wse/bUcqojTWpWQlgsc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lSdcllDXx8MA+s0GULjDA1lQkcV0L8/aHtZ6dM2pZ2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HGr7JLTTA7ksAnlmjSIwwdBVvgr3fv46/FTdiCPYpos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mMr25v1VwOEVZ8xaNUTHJCcsYqV+kwK6RzGYilxPtJ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "129hJbziPJzNo0IoTU3bECdge0FtaPW8dm4dyNVNwYU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "doiLJ96qoo+v7NqIAZLq6BI5axV8Id8gT5vyJ1ZZ0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cW/Lcul3xYmfyvI/0x/+ybN78aQmBK1XIGs1EEU09N8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1aVIwzu9N5EJV9yEES+/g6hOTH7cA2NTcLIc59cu0wU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kw5tyl7Ew0r1wFyrN1mB9FiVW2hK2BxxxUuJDNWjyjQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ADAY2YBrm6RJBDY/eLLcfNxmSJku+mefz74gH66oyco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8gkqB1LojzPrstpFG7RHYmWxXpIlPDTqWnNsXH7XDRU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TESfVQMDQjfTZmHmUeYUE2XrokJ6CcrsKx/GmypGjOw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qFM+HFVQ539S0Ouynd1fBHoemFxtU9PRxE5+Dq7Ljy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jPiFgUZteSmOg4wf3bsEKCZzcnxmMoILsgp/GaZD+dM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YaWUgJhYgPNN7TkFK16H8SsQS226JguaVhOIQxZwQNQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x90/Qk3AgyaFsvWf2KUCu5XF3j76WFSjt/GrnG01060=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZGWybWL/xlEdMYRFCZDUoz10sywTf7U/7wufsb78lH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8l4ganN66jIcdxfHAdYLaym/mdzUUQ8TViw3MDRySPc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c8p5XEGTqxqvRGVlR+nkxw9uUdoqDqTB0jlYQ361qMA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZGFLlpQBcU3zIUg8MmgWwFKVz/SaA7eSYFrfe3Hb70=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "34529174M77rHr3Ftn9r8jU4a5ztYtyVhMn1wryZSkU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YkQ4pxFWzc49MS0vZM6S8mNo4wAwo21rePBeF3C+9mI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MhOf4mYY00KKVhptOcXf0bXB7WfuuM801MRJg4vXPgc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7pbbD8ihNIYIBJ3tAUPGzHpFPpIeCTAk5L88qCB0/9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C9Q5PoNJTQo6pmNzXEEXUEqH22//UUWY1gqILcIywec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AqGVk1QjDNDLYWGRBX/nv9QdGR2SEgXZEhF0EWBAiSE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/sGI3VCbJUKATULJmhTayPOeVW+5MjWSvVCqS77sRbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yOtbL0ih7gsuoxVtRrACMz+4N5uo7jIR7zzmtih2Beo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uA6dkb2Iyg9Su8UNDvZzkPx33kPZtWr/CCuEY+XgzUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1DoSFPdHIplqZk+DyWAmEPckWwXw/GdB25NLmzeEZhk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OfDVS0T3ZuIXI/LNbTp6C9UbPIWLKiMy6Wx+9tqNl+g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3PZjHXbmG6GtPz+iapKtQ3yY4PoFFgjIy+fV2xQv1YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kaoLN0BoBWsmqE7kKkJQejATmLShd8qffcAmlhsxsGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpiw9KgQdegGmp7IJnSGX2miujRLU0xzs0ITTqbPW7c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NuXFf7xGUefYjIUTuMxNUTCfVHrF8oL0AT7dPv5Plk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8Tz53LxtfEBJ9eR+d2690kwNsqPV6XyKo2PlqZCbUrc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e6zsOmHSyV8tyQtSX6BSwui6wK9v1xG3giY/IILJQ2w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fedFMCxa2DzmIpfbDKGXhQg0PPwbUv6vIWdwwlvhms=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yEJKMFnWXTC8tJUfzCInzQRByNEPjHxpw4L4m8No91Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YbFuWwOiFuQyOzIJXDbOkCWC2DyrG+248TBuVCa1pXU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w7IkwGdrguwDrar5+w0Z3va5wXyZ4VXJkDMISyRjPGo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YmJUoILTRJPhyIyWyXJTsQ6KSZHHbEpwPVup6Ldm/Ko=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FvMjcwVZJmfh6FP/yBg2wgskK+KHD8YVUY6WtrE8xbg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4HCtD4HyYz0nci49IVAa10Z4NJD/FHnRMV4sRX6qro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nC7BpXCmym+a0Is2kReM9cYN2M1Eh5rVo8fjms14Oiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1qtVWaeVo649ZZZtN8gXbwLgMWGLhz8beODbvru0I7Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ej+mC0QFyMNIiSjR939S+iGBm7dm+1xObu5IcF/OpbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UQ8LbUG3cMegbr9yKfKanAPQE1EfPkFciVDrNqZ5GHY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4iI3mXIDjnX+ralk1HhJY43mZx2uTJM7hsv9MQzTX7E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0WQCcs3rvsasgohERHHCaBM4Iy6yomS4qJ5To3/yYiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qDCTVPoue1/DOAGNAlUstdA9Sid8MgEY4e5EzHcVHRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9F9Mus0UnlzHb8E8ImxgXtz6SU98YXD0JqswOKw/Bzs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pctHpHKVBBcsahQ6TNh6/1V1ZrqOtKSAPtATV6BJqh0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vfR3C/4cPkVdxtNaqtF/v635ONbhTf5WbwJM6s4EXNE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ejP43xUBIex6szDcqExAFpx1IE/Ksi5ywJ84GKDFRrs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jbP4AWYd3S2f3ejmMG7dS5IbrFol48UUoT+ve3JLN6U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CiDifI7958sUjNqJUBQULeyF7x0Up3loPWvYKw9uAuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e2dQFsiHqd2BFHNhlSxocjd+cPs4wkcUW/CnCz4KNuM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PJFckVmzBipqaEqsuP2mkjhJE4qhw36NhfQ9DcOHyEU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S3MeuJhET/B8VcfZYDR9fvX0nscDj416jdDekhmK11s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CGVHZRXpuNtQviDB2Kj03Q8uvs4w3RwTgV847R7GwPw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yUGgmgyLrxbEpDVy89XN3c2cmFpZXWWmuJ/35zVZ+Jw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "inb6Q97mL1a9onfNTT8v9wsoi/fz7KXKq3p8j90AU9c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CCyYx/4npq9xGO1lsCo8ZJhFO9/tN7DB+/DTE778rYg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LNnYw4fwbiAZu0kBdAHPEm/OFnreS+oArdB5O/l/I98=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P006SxmUS/RjiQJVYPdMFnNo3827GIEmSzagggkg05Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oyvwY+WsnYV6UHuPki1o0ILJ2jN4uyXf9yaUNtZJyBA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "36Lk3RHWh1wmtCWC/Yj6jNIo17U5y6SofAgQjzjVxD8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vOOo8FqeHnuO9mqOYjIb4vgwIwVyXZ5Y+bY5d9tGFUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bJiDJjwQRNxqxlGjRm5lLziFhcfTDCnQ/qU1V85qcRg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2Qgrm1n0wUELAQnpkEiIHB856yv76q8jLbpiucetcm0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5ciPOYxTK0WDwwYyfs7yiVymwtYQXDELLxmM4JLl4/o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "31dC2WUSIOKQc4jwT6PikfeYTwi80mTlh7P31T5KNQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YluTV2Mu53EGCKLcWfHZb0BM/IPW2xJdG3vYlDMEsM4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dh/8lGo2Ek6KukSwutH6Q35iy8TgV0FN0SJqe0ZVHN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EVw6HpIs3BKen2qY2gz4y5dw1JpXilfh07msZfQqJpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FYolLla9L8EZMROEdWetozroU40Dnmwwx2jIMrr7c1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8M6k4QIutSIj6CM41vvkQtuFsaGrjoR9SZJVSLbfGKQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9LM0VoddDNHway442MqY+Z7vohB2UHau/cddshhzf40=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66i8Ytco4Yq/FMl6pIRZazz3CZlu8fO2OI6Pne0pvHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2a/HgX+MjZxjXtSvHgF1yEpHMJBkl8Caee8XrJtn0WM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "frhBM662c4ZVG7mWP8K/HhRjd01lydW/cPcHnDjifqc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6k1T7Q1t668PBqv6fwpVnT1HWh7Am5LtbKvwPJKcpGU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UlJ5Edfusp8S/Pyhw6KTglIejmbr1HO0zUeHn/qFETA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jsxsB+1ECB3assUdoC333do9tYH+LglHmVSJHy4N8Hg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2nzIQxGYF7j3bGsIesECEOqhObKs/9ywknPHeJ3yges=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xJYKtuWrX90JrJVoYtnwP7Ce59XQGFYoalxpNfBXEH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NLI5lriBTleGCELcHBtNnmnvwSRkHHaLOX4cKboMgTw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hUOQV0RmE5aJdJww1AR9rirJG4zOYPo+6cCkgn/BGvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4G2Of76AgxcUziBwCyH+ayMOpdBWzg4yFrTfehSC2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VuamM75RzGfQpj2/Y1jSVuQLrhy6OAwlZxjuQLB/9Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn9+hLq7hvw02xr9vrplOCDXKBTuFhfbX7d5v/l85Pg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fAiGqKyLZpGngBYFbtYUYt8LUrJ49vYafiboifTDjxs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BxRILymgfVJCczqjUIWXcfrfSgrrYkxTM5VTg0HkZLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CrFY/PzfPU2zsFkGLu/dI6mEeizZzCR+uYgjZBAHro0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AEbrIuwvXLTtYgMjOqnGQ8y8axUn5Ukrn7UZRSyfQVw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ouWeVH3PEFg+dKWlXc6BmqirJOaVWjJbMzZbCsce4dA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+hd6xFB+EG+kVP7WH4uMd1CLaWMnt5xJRaY/Guuga9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zmpGalfAOL3gmcUMJYcLYIRT/2VDO/1Dw4KdYZoNcng=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2PbHAoM/46J2UIZ/vyksKzmVVfxA7YUyIxWeL/N/vBk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fD9x+zk5MVFesb59Klqiwwmve7P5ON/5COURXj5smE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tlrNQ4jaq051iaWonuv1sSrYhKkL1LtNZuHsvATha3s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fBodm28iClNpvlRyVq0dOdXQ08S7/N3aDwid+PdWvRo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O+/nnRqT3Zv7yMMGug8GhKHaWy6u7BfRGtZoj0sdN1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5AZZ/RTMY4Photnm/cpXZr/HnFRi3eljacMsipkJLHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oFVyo/kgoMxBIk2VE52ySSimeyU+Gr0EfCwapXnTpKA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z8v59DfcnviA0mzvnUk+URVO0UuqAWvtarEgJva/n1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P64GOntZ+zBJEHkigoh9FSxSO+rJTqR20z5aiGQ9an4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xMbSuDPfWuO/Dm7wuVl06GnzG9uzTlJJX9vFy7boGlY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kXPB19mRClxdH2UsHwlttS6lLU2uHvzuZgZz7kC45jU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NDVjVYXAw4k0w4tFzvs7QDq39aaU3HQor4I2XMKKnCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uKw/+ErVfpTO1dGUfd3T/eWfZW3nUxXCdBGdjvHtZ88=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "av0uxEzWkizYWm0QUM/MN1hLibnxPvCWJKwjOV4yVQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ERwUC47dvgOBzIsEESMIioLYbFOxOe8PtJTnmDkKuHM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2gseKlG5Le12fS/vj4eaED4lturF16kAgJ1TpW3HxEE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7Cvg0Y3j/5i2F1TeXxlMmU7xwif5dCmwkZAOrVC5K2Y=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-InsertFind.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-InsertFind.json new file mode 100644 index 000000000..54e43e4a2 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-InsertFind.json @@ -0,0 +1,1893 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Decimal. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RGTjNVEsNJb+DG7DpPOam8rQWD5HZAMpRyiTQaw7tk8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RlQWwhU+uVv0a+9IB5cUkEfvHBvOw3B1Sx6WfPWMqes=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubb81XTC7U+4tcNzf1oYvOY6gR5hC2Izqx54f4GuJ0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6M4Q5NMQ9TqNnjzGOxIkiUIY8TEL0I3XD1QnhefQUqU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BtInzk9t2FFMCEY6AQ7zN8jwrrZEs2irSv6q0Q4NaIw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vxXfETu9cuBIpRBo3jUUU04mJIH/aAhLX8K6VI5Xv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXPCdS+q23zi1bkPnaVG2j0PsVtxdeSLJ//h6J1x8RU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KY3KkfBAsN2l80wbpj41G0gwBR5KmmFnZcagg7D3ENk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI8NFAxXCX4VOnY5X73K6KI/Yspd3aR94KV39MhJlAw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nFxH0UC3mATKA6Vboz+QX/hAjj19kF/SH6H5Cne7qC0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q8hYqIYaIi7nOdG/7qQZYnz8Bsacfi66M1nVku4SH08=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4saA92R4arp4anvD9xFtze+sNcQqTEhPHyl1h70A8NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DbIziOBRRyeQS6RtBR09E37LV+CTKrEjGoRMLSpG6eE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Fv80Plp/7w2gnVqrwawLd6qhJ10G4NCDm3re67cNq4Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T/T2oiQCBBES4YN7EodzPRdabZSFlYIClHBym+bQUZE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQgHD3l46Ujqtbnj1VbbeM29C9wJzOhz+yZ/7XdSrxk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ltlFKzWvyZvHxDFOYDd/XXJ6kUiJj0ln2HTCEz2o4Z4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "flW8A7bltC1u8bzx0WJtxosGJdOVsJFfbx33jxnpFGg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SXO+92QbMKwUSG2t27ciunV1c3VvFkUuDmSczpRe008=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+KioGs1GM+xRBzFE67ePTWj04KMSE5/Y6qUF7nJ5kvU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L3xNVbh6YH+RzqABN+5Jgb7T234Efpn766DmUvxIxgg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hPF+60mBYPjh21dEmPlBhKgyc9S2qLtTkypYvnqP2Fc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EletRsETy2HcjaPIm2c8CkT7ch/P3pJJDC8hasepcSU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "r5bMXUaNKqLPxZ+TG9HYTG4aSDgcpim27rN8rQFkM0w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Q7Erdr8+/S0wUEDDIqlS5XjBVWvhZY65K0uUDb6+Ns=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xEcnhXy35hbXNVBPOOt3TUHbxvKfQ48KjA9b6/rbMqQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "T8bEpiQNgsEudXvyKE9SZlSvbpV/LUaslsdqgSFltyo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hIoiaF2YjnxDbODfhFEB+JGZ5nf8suD3Shck5bwQ3N0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qnA6qzejeRJ0rsZaZ0zOvKAaXyxt5lpscKQNYFZNl4k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "anAKCL2DN/le2VaP0n2ucYSEH/DaaEH/8Sa4OqTZsRA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JCZlBJaFm618oWYSnT9Jr1MtwFVw4BZjOzO+5yWgR90=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yxyk4n9762WzcDVGnTn4jCqUnSMIVCrLDIjCX1QVj34=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fDI6fdKvDJwim5/CQwWZEzcrXE3LHgy7FTtffcC7tXE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Vex+gcz5T+WkzsVZQrkqUR2ryyZbnaOGuWpYvjN0zCw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8TLEXz+Gbbp6llHpZXVjLsdlYY9f6hrKpHVpyfDe0RY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fTyt5BrunypS65TfOzFW2E2qdIuT4SLeDeGlbQoJCs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8fKGrkqN0/KuSjyXgDBmRauDKrSa//JBKRWHEB9xBf4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s4codmG7uN4ss6P357jL21lazEe90M9GOK5WrOknSV0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RkSpua8XF+NUdxVDU90EbLUTTyZFX3tt3atBTroFaRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LnTCuCDyAHK5B9KXzjtwGmWB+qergQk2OCjnIx9MI2A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cBFh0virAX4pVXf/udIGI2951i0+0aZAdJcBVGtYnT4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "G54X6myQXWZ5fw/G31en3QbdgfXzL9+hFTtJpnWMqDI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EdsiiuezcsFJFnYIyGjCOhnqMj1BOwTB5EFxN+ERUkg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dVH9MXLtk0WTwGQ3xmrhOqfropMUkDW3o6paNPGl3NU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sB3HqXKWY3pKbuEH8BTbfNIGfbY+7/ZbOc3XC+JRNNI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WHyDk62Xhqbo4/iie2aLIM4x2uuAjv6102dJSHI58oM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pNUFuHpeNRDUZ/NrtII2c6sNc9eGR1lIUlIyXKERA+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UPa+pdCqnN0bfAptdzldQOSd01gidrDKy8KhWrpSKAI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l+7dOAlo+HUffMqFYXL6pgUFeTbwOM9CjKQLxEoLtc4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SRnDXV/rN6C8xwMutv9E1luv3DOUio3VkgPr8Cpm7Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QcH6gl+gX7xZ7OWhUNQMbndJy0Piz49pDo6RsnLkVSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "t+uL4DnfsI/Zll/KXWW1cOKX3Hu8WIkm3pt9efCVSAQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "myutHDctku/+Uug/nD8gRbYvmx/IovtoAAC2/fz2oHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6C+cjD0e0nSCP6cPqQYbNG7SlOd6Mfvi8hyfm7Ng+D8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zg01JSoOj9oBKT0S1ldJucXzY5AKgreS+h2xJreWTOs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7qQ80/FjodHl1m1py/Oii0/9C/xWbLdhaRXQ+kkCP10=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YwWMNH07vL6c5Nhg+MRnVByhzUunu8y0VLM9z/XvR5U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dle8bU98+fudAbc14SToZFkwvV3tcYVsjDug0NWljpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "J+eKL1vPJmlzltvhI6Li5Fz/TJmi3Ng+ehRTcs46API=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB3XzfFygLwC3WHkj0up+VbEd25KKoce1vOpG/5bwK4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vnVnmOnL+z2pqwE+A6cVKS0Iwy4F4/2IiElJca9bUQM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+lG5r/Fpqry3BtFuvY67+RntmHAMDoLVOSGc6ZoXPb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L5MXQertqc6uj7ADe8aWKbd1sYHPCE7P1VYVg9Zc3VI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "imKONuZgopt0bhM3GMX2WVPwQYMTobuUUEdhcLfHs4c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eOkU1J1uVbiVFWBerbXsSIVcF2nqiicTkFy4x7kFHB8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gI0uDhXeoH/UatDQKEf4qo8FHzWZDhb/wuWTqbq/ID4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cOkd5Aa3btYhtojE/smsF/PJnULqQ4NNqTkU6KXTFmo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AWNJMs1MTe294oFipp8Y6P0CjpkZ4qCZoClQF3XcHq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6gJtlzXOFhGYrVbTuRMmvMlDTwXdNtR9aGBlHZPwIMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LEmwVGA/xsEG7UrcOoYLFu6KCXgijzFznenknuDacm8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mIRFPTXRrGaPtp/Ydij2jgkRe4uoUvAKxW2d8b9zYL0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+Uv2u48WALOO0L311z+eryjYQzKJVMfdHMZPhOAFmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "INXXp0wDyVCq+NtfIrrC2ciETmyW/dWB/48/u4yLEZ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "se7DGo8XrlrQDLEcco1tZrQt9kDe+0RTyl2bw/quG4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vr0m2+Zk9lbN6UgWCyn8xJWJOokU3IDYab5U5q1+CgQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XI+eJ8Gy2JktG1gICgoj1qpsfy1tKmH0kglWbaQH6DA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A+UCuNnuAUqnQzspA6TVqUPRmtZmpSex5HFw7THRxs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaH2Ehfljd19uo0Fvb3iwkdaiWEVQd2YPoitgEPkhSM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S/iZBJGcc8+qZxyMtab65MMBoSglybwk3x58Nb86gnY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w14ZE5qqY5YgkS4Zcs9YNbrQbY1XfGOOHNn9bOYnFVQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0MhGd/jEF1vjkKGp+ZMn9SjLK54jkp9W4Hg+Sp/oxaI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92QZ73e/NRTYgCm4aifaKth6aAsKnLLccBc0zx/qUTY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WOjzemCgFJOiGIp81RSVh/tFlzSTj9eFWcBnsiv2Ycs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DrsP9CmfKPjw5yLL8bnSeAxfNzAwlb+Z8OqCiKgBY7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lMogqg8veBv6mri3/drMe9afJiKMvevkmGcw9BedfLo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TxqwNcY8Tg2MPpNdkPBwvfpuTttSYRHU26DGECKYQ9o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "l0u1b4b4vYACWIwfnB7PZac4oDEgjQZCzHruNPTgAIY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iVSGQ+cCfhbWIrY/v/WBORK92elu9gfRKyGhr6r/k00=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yK1forG50diEXte8ECzjfpHeYsPyuQ/dgxbxn/nzY5k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gIfTLCD3VwnOwkC0zPXWTqaITxX6ZplA69PO2a6zolc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O/Zxlgh3WqpzJ7+Sd8XWMVID4/GXJUUWaSqfgDUi3b0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZQ6yv368zwahUqSUYH/StL0Qgz/TwS1CzlMjVDvCciI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m2rPEYkjwyiKdonMrKlcF7hya4lFOAUwEePJ3SgrNx8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mq0yl5iVKlq71bT/dT/fXOWf2n90bTnXFnOdGDN0JOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6qDGMXipPLC2O6EAAMjO2F9xx4rdqZso4IkPpH2304U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jvQHRQQa2RIszE2LX2Hv2LbRhYawJ6qmtRt8HZzFQXg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ovJXQrkZlpeHRciKyE/WWNm5O389gRgzx1W+Dw596X4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "a4kgRNvYctGYqyQv9qScL/WkljTYVylJ9pE9KDULlxU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qV4Q48vPiCJMTjljotzYKI/zfExWpkKOSHGcAjGyDig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jtI7zbBF+QW/aYYTkn90zzyHLXLgmy7l1bzgMb2oqic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q0KmJl9txPdn962UNvnfe6UFhdk9YaFZuTm33F+csso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ULNdEqeZJgtmNOhN/Y9INzsE9AnxWYwOMn+pIbRXIFs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "R4oz9+wkdjpKe5tE1jpG7IURAnfvS5fLP4LrD5cZfTE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qG5Z7VhwSu/HT/YFTgDzyAAzJKq51xPw2HeEV5btYC4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OM/1DmIIZ5Qyhtq8TGkHTBEMVKjAnKRZMRXYtTG8ctc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2R5vZbljLXnDFA99YfGuRB7pAdPJVKsT25zLNMC0fUk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OMbavF2EmdAz1fHkLV3ctFEUDfriKhoT2gidwHZ9z1o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MWT4Zrw3/vVvTYMa1Is5Pjr3wEwnBfnEAPPUAHKQhNU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tBkRPfG9yxfKocQx5pAJX0oEHKPL0Tgtr+0UYe09InE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lqxpnDR/H0YgH7RcfKoNoaaRhe1SIazIeMbQ1fu9y3Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "utT1UdR22PWOTrOkZauztX613lAplV4eh/ejTRb7ZSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S+Y2yFyKi/a6FXhih4yGo29X8I8OT6/zwEoX6NMKT4o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QSjVppg29x6oS5yBg8OFjrFt0tuTpWCuKxfIy0k8YnE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y3r6/Xsfvsl3HksXlVYkJgHUqpQGfICxg3x9f8Zw1qM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BSltHzEwDjFN4du9rDHAPvl22atlcTioEtt+gC5L1tk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0arGXjSN0006UnXbrWsGqhvBair569DeFDUME3Df3rA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s/DumaMad08S+PBUUcrS+v42K0z8HgcdiQtrFAEu2Qs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EzJ8Y8N0OQBTlnvrK82PdevDNZZO4E6CNgYVu8Cj6Ks=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VA4vr8jBPI5QdiPrULzzZjBMIUbG3V7Slg5zm0bFcKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YAOvEB2ZLtq9LQiFViBHWaxxWVVonC2rNYj9tN9s3L0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hgaHMo9aAGS+nBwvqnTjZO+YkiQPY1c1XcIYeaYKHyI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YvaoLt3ZpH0atB0tNzwMjpoxRYJXl0DqSjisMJiGVBE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EMmW6CptFsiLoPOi5/uAJQ2FmeLg6mCpuVLLrRWk7Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1jQsNMarSnarlYmXEuoFokeBMg/090qUD9wqo1Zn8Gs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hupXNKhRpJxpyDAAP1TgJ5JMZh9lhbMk6s7D7dMS3C8=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Update.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Update.json new file mode 100644 index 000000000..b2b8136a9 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Decimal-Update.json @@ -0,0 +1,1910 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Decimal. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rbf3AeBEv4wWFAKknqDxRW5cLNkFvbIs6iJjc6LShQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0l86Ag5OszXpa78SlOUV3K9nff5iC1p0mRXtLg9M1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hn6yuxFHodeyu7ISlhYrbSf9pTiH4TDEvbYLWjTwFO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zdf4y2etKBuIpkEU1zMwoCkCsdisfXZCh8QPamm+drY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rOQ9oMdiK5xxGH+jPzOvwVqdGGnF3+HkJXxn81s6hp4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "61aKKsE3+BJHHWYvs3xSIBvlRmKswmaOo5rygQJguUg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KuDb/GIzqDM8wv7m7m8AECiWJbae5EKKtJRugZx7kR0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Q+t8t2TmNUiCIorVr9F3AlVnX+Mpt2ZYvN+s8UGict8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJRZIpKxUgHyL83kW8cvfjkxN3z6WoNnUg+SQw+LK+k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnUsYjip8SvW0+m9mR5WWTkpK+p6uwJ6yBUAlBnFKMk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PArHlz+yPRYDycAP/PgnI/AkP8Wgmfg++Vf4UG1Bf0E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wnIh53Q3jeK8jEBe1n8kJLa89/H0BxO26ZU8SRIAs9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4F8U59gzBLGhq58PEWQk2nch+R0Va7eTUoxMneReUIA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ihKagIW3uT1dm22ROr/g5QaCpxZVj2+Fs/YSdM2Noco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EJtUOOwjkrPUi9mavYAi+Gom9Y2DuFll7aDwo4mq0M0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dIkr8dbaVRQFskAVT6B286BbcBBt1pZPEOcTZqk4ZcI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aYVAcZYkH/Tieoa1XOjE/zCy5AJcVTHjS0NG2QB7muA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sBidL6y8TenseetpioIAAtn0lK/7C8MoW4JXpVYi3z8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0Dd2klU/t4R86c2WJcJDAd57k/N7OjvYSO5Vf8KH8sw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I3jZ92WEVmZmgaIkLbuWhBxl7EM6bEjiEttgBJunArA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aGHoQMlgJoGvArjfIbc3nnkoc8SWBxcrN7hSmjMRzos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bpiWPnF/KVBQr5F6MEwc5ZZayzIRvQOLDAm4ntwOi8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tI7QVKbE6avWgDD9h4QKyFlnTxFCwd2iLySKakxNR/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XGsge0CnoaXgE3rcpKm8AEeku5QVfokS3kcI+JKV1lk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JQxlryW2Q5WOwfrjAnaZxDvC83Dg6sjRVP5zegf2WiM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YFuHKJOfoqp1iGVxoFjx7bLYgVdsN4GuUFxEgO9HJ5s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z6vUdiCR18ylKomf08uxcQHeRtmyav7/Ecvzz4av3k4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SPGo1Ib5AiP/tSllL7Z5PAypvnKdwJLzt8imfIMSEJQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m94Nh6PFFQFLIib9Cu5LAKavhXnagSHG6F5EF8lD96I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pfEkQI98mB+gm1+JbmVurPAODMFPJ4E8DnqfVyUWbSo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DNj3OVRLbr43s0vd+rgWghOL3FqeO/60npdojC8Ry/M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kAYIQrjHVu49W8FTxyxJeiLVRWWjC9fPcBn+Hx1F+Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "aCSO7UVOpoQvu/iridarxkxV1SVxU1i9HVSYXUAeXk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Gh6hTP/yj1IKlXQ+Q69KTfMlGZjEcXoRLGbQHNFo/1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/gDgIFQ4tAlJk3GN48IS5Qa5IPmErwGk8CHxAbp6gs0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PICyimwPjxpusyKxNssOOwUotAUbygpyEtORsVGXT8g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4lu+cBHyAUvuxC6JUNyHLzHsCogGSWFFnUCkDwfQdgI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pSndkmoNUJwXjgkbkgOrT5f9nSvuoMEZOkwAN9ElRaE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tyW+D4i26QihNM5MuBM+wnt5AdWGSJaJ4X5ydc9iWTU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9Syjr8RoxUgPKr+O5rsCu07AvcebA4P8IVKyS1NVLWc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "67tPfDYnK2tmrioI51fOBG0ygajcV0pLo5+Zm/rEW7U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "y0EiPRxYTuS1eVTIaPQUQBBxwkyxNckbePvKgChwd0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NWd+2veAaeXQgR3vCvzlI4R1WW67D5YsVLdoXfdb8qg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PY5RQqKQsL2GqBBSPNOEVpojNFRX/NijCghIpxD6CZk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lcvwTyEjFlssCJtdjRpdN6oY+C7bxZY+WA+QAqzj9zg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWE7XRNylvTwO/9Fv56dNqUaQWMmESNS/GNIwgBaEI0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ijwlrUeS8nRYqK1F8kiCYF0mNDolEZS+/lJO1Lg93C8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8KzV+qYGYuIjoNj8eEpnTuHrMYuhzphl80rS6wrODuU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wDyTLjSEFF895hSQsHvmoEQVS6KIkZOtq1c9dVogm9I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SGrtPuMYCjUrfKF0Pq/thdaQzmGBMUvlwN3ORIu9tHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KySHON3hIoUk4xWcwTqk6IL0kgjzjxgMBObVIkCGvk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hBIdS9j0XJPeT4ot73ngELkpUoSixvRBvdOL9z48jY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Tx6um0q9HjS5ZvlFhvukpI6ORnyrXMWVW1OoxvgqII0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zFKlyfX5H81+d4A4J3FKn4T5JfG+OWtR06ddyX4Mxas=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cGgCDuPV7MeMMYEDpgOupqyNP4BQ4H7rBnd2QygumgM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IPaUoy98v11EoglTpJ4kBlEawoZ8y7BPwzjLYBpkvHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Pfo4Am6tOWAyZNn8G9W5HWWGC3ZWmX0igI/RRB870Ro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fnTSjd7bC1Udoq6iM7UDnHAC/lsIXSHp/Gy332qw+/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fApBgVRrTDyEumkeWs5p3ag9KB48SbU4Si0dl7Ns9rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QxudfBItgoCnUj5NXVnSmWH3HK76YtKkMmzn4lyyUYY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sSOvwhKa29Wq94bZ5jGIiJQGbG1uBrKSBfOYBz/oZeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FdaMgwwJ0NKsqmPZLC5oE+/0D74Dfpvig3LaI5yW5Fs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sRWBy12IERN43BSZIrnBfC9+zFBUdvjTlkqIH81NGt4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/4tIRpxKhoOwnXAiFn1Z7Xmric4USOIfKvTYQXk3QTc=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mr/laWHUijZT5VT3x2a7crb7wgd/UXOGz8jr8BVqBpM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VDCpBYsJIxTfcI6Zgf7FTmKMxUffQv+Ys8zt5dlK76I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zYDslUwOUVNwTYkETfjceH/PU3bac9X3UuQyYJ19qK0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rAOmHSz18Jx107xpbv9fYcPOmh/KPAqge0PAtuhIRnc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BFOB1OGVUen7VsOuS0g8Ti7oDsTt2Yj/k/7ta8YAdGM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fckE5SPs0GU+akDkUEM6mm0EtcV3WDE/sQsnTtodlk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mi9+aNjuwIvaMpSHENvKzKRAmX9cYguo2mXLvOoftHQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K6TWn4VcWWkz/gkUkLmbtwkG7SNeABICmLDnoYJFlLU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z+2/cEtGU0Fq7QJFNGA/0y4aWAsw0ncG6X0LYRqwS3c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rrSIf+lgcNZFbbUkS9BmE045jRWBpcBJXHzfMVEFuzE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KlHL3Kyje1/LMIfgbCqw1SolxffJvvgsYBV5y77wxuA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hzJ1YBoETmYeCh352dBmG8d8Wse/bUcqojTWpWQlgsc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lSdcllDXx8MA+s0GULjDA1lQkcV0L8/aHtZ6dM2pZ2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HGr7JLTTA7ksAnlmjSIwwdBVvgr3fv46/FTdiCPYpos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mMr25v1VwOEVZ8xaNUTHJCcsYqV+kwK6RzGYilxPtJ4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "129hJbziPJzNo0IoTU3bECdge0FtaPW8dm4dyNVNwYU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "doiLJ96qoo+v7NqIAZLq6BI5axV8Id8gT5vyJ1ZZ0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cW/Lcul3xYmfyvI/0x/+ybN78aQmBK1XIGs1EEU09N8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1aVIwzu9N5EJV9yEES+/g6hOTH7cA2NTcLIc59cu0wU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kw5tyl7Ew0r1wFyrN1mB9FiVW2hK2BxxxUuJDNWjyjQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ADAY2YBrm6RJBDY/eLLcfNxmSJku+mefz74gH66oyco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8gkqB1LojzPrstpFG7RHYmWxXpIlPDTqWnNsXH7XDRU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TESfVQMDQjfTZmHmUeYUE2XrokJ6CcrsKx/GmypGjOw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qFM+HFVQ539S0Ouynd1fBHoemFxtU9PRxE5+Dq7Ljy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jPiFgUZteSmOg4wf3bsEKCZzcnxmMoILsgp/GaZD+dM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YaWUgJhYgPNN7TkFK16H8SsQS226JguaVhOIQxZwQNQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x90/Qk3AgyaFsvWf2KUCu5XF3j76WFSjt/GrnG01060=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZGWybWL/xlEdMYRFCZDUoz10sywTf7U/7wufsb78lH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8l4ganN66jIcdxfHAdYLaym/mdzUUQ8TViw3MDRySPc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c8p5XEGTqxqvRGVlR+nkxw9uUdoqDqTB0jlYQ361qMA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZGFLlpQBcU3zIUg8MmgWwFKVz/SaA7eSYFrfe3Hb70=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "34529174M77rHr3Ftn9r8jU4a5ztYtyVhMn1wryZSkU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YkQ4pxFWzc49MS0vZM6S8mNo4wAwo21rePBeF3C+9mI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MhOf4mYY00KKVhptOcXf0bXB7WfuuM801MRJg4vXPgc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7pbbD8ihNIYIBJ3tAUPGzHpFPpIeCTAk5L88qCB0/9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C9Q5PoNJTQo6pmNzXEEXUEqH22//UUWY1gqILcIywec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AqGVk1QjDNDLYWGRBX/nv9QdGR2SEgXZEhF0EWBAiSE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/sGI3VCbJUKATULJmhTayPOeVW+5MjWSvVCqS77sRbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yOtbL0ih7gsuoxVtRrACMz+4N5uo7jIR7zzmtih2Beo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uA6dkb2Iyg9Su8UNDvZzkPx33kPZtWr/CCuEY+XgzUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1DoSFPdHIplqZk+DyWAmEPckWwXw/GdB25NLmzeEZhk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OfDVS0T3ZuIXI/LNbTp6C9UbPIWLKiMy6Wx+9tqNl+g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3PZjHXbmG6GtPz+iapKtQ3yY4PoFFgjIy+fV2xQv1YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kaoLN0BoBWsmqE7kKkJQejATmLShd8qffcAmlhsxsGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpiw9KgQdegGmp7IJnSGX2miujRLU0xzs0ITTqbPW7c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NuXFf7xGUefYjIUTuMxNUTCfVHrF8oL0AT7dPv5Plk4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8Tz53LxtfEBJ9eR+d2690kwNsqPV6XyKo2PlqZCbUrc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e6zsOmHSyV8tyQtSX6BSwui6wK9v1xG3giY/IILJQ2w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2fedFMCxa2DzmIpfbDKGXhQg0PPwbUv6vIWdwwlvhms=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yEJKMFnWXTC8tJUfzCInzQRByNEPjHxpw4L4m8No91Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YbFuWwOiFuQyOzIJXDbOkCWC2DyrG+248TBuVCa1pXU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "w7IkwGdrguwDrar5+w0Z3va5wXyZ4VXJkDMISyRjPGo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YmJUoILTRJPhyIyWyXJTsQ6KSZHHbEpwPVup6Ldm/Ko=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FvMjcwVZJmfh6FP/yBg2wgskK+KHD8YVUY6WtrE8xbg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4HCtD4HyYz0nci49IVAa10Z4NJD/FHnRMV4sRX6qro=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nC7BpXCmym+a0Is2kReM9cYN2M1Eh5rVo8fjms14Oiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1qtVWaeVo649ZZZtN8gXbwLgMWGLhz8beODbvru0I7Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ej+mC0QFyMNIiSjR939S+iGBm7dm+1xObu5IcF/OpbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UQ8LbUG3cMegbr9yKfKanAPQE1EfPkFciVDrNqZ5GHY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4iI3mXIDjnX+ralk1HhJY43mZx2uTJM7hsv9MQzTX7E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0WQCcs3rvsasgohERHHCaBM4Iy6yomS4qJ5To3/yYiw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qDCTVPoue1/DOAGNAlUstdA9Sid8MgEY4e5EzHcVHRk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9F9Mus0UnlzHb8E8ImxgXtz6SU98YXD0JqswOKw/Bzs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pctHpHKVBBcsahQ6TNh6/1V1ZrqOtKSAPtATV6BJqh0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vfR3C/4cPkVdxtNaqtF/v635ONbhTf5WbwJM6s4EXNE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ejP43xUBIex6szDcqExAFpx1IE/Ksi5ywJ84GKDFRrs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jbP4AWYd3S2f3ejmMG7dS5IbrFol48UUoT+ve3JLN6U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CiDifI7958sUjNqJUBQULeyF7x0Up3loPWvYKw9uAuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e2dQFsiHqd2BFHNhlSxocjd+cPs4wkcUW/CnCz4KNuM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PJFckVmzBipqaEqsuP2mkjhJE4qhw36NhfQ9DcOHyEU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "S3MeuJhET/B8VcfZYDR9fvX0nscDj416jdDekhmK11s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CGVHZRXpuNtQviDB2Kj03Q8uvs4w3RwTgV847R7GwPw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yUGgmgyLrxbEpDVy89XN3c2cmFpZXWWmuJ/35zVZ+Jw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "inb6Q97mL1a9onfNTT8v9wsoi/fz7KXKq3p8j90AU9c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CCyYx/4npq9xGO1lsCo8ZJhFO9/tN7DB+/DTE778rYg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "LNnYw4fwbiAZu0kBdAHPEm/OFnreS+oArdB5O/l/I98=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P006SxmUS/RjiQJVYPdMFnNo3827GIEmSzagggkg05Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oyvwY+WsnYV6UHuPki1o0ILJ2jN4uyXf9yaUNtZJyBA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "36Lk3RHWh1wmtCWC/Yj6jNIo17U5y6SofAgQjzjVxD8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vOOo8FqeHnuO9mqOYjIb4vgwIwVyXZ5Y+bY5d9tGFUM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bJiDJjwQRNxqxlGjRm5lLziFhcfTDCnQ/qU1V85qcRg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2Qgrm1n0wUELAQnpkEiIHB856yv76q8jLbpiucetcm0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5ciPOYxTK0WDwwYyfs7yiVymwtYQXDELLxmM4JLl4/o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "31dC2WUSIOKQc4jwT6PikfeYTwi80mTlh7P31T5KNQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YluTV2Mu53EGCKLcWfHZb0BM/IPW2xJdG3vYlDMEsM4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dh/8lGo2Ek6KukSwutH6Q35iy8TgV0FN0SJqe0ZVHN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EVw6HpIs3BKen2qY2gz4y5dw1JpXilfh07msZfQqJpc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FYolLla9L8EZMROEdWetozroU40Dnmwwx2jIMrr7c1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8M6k4QIutSIj6CM41vvkQtuFsaGrjoR9SZJVSLbfGKQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9LM0VoddDNHway442MqY+Z7vohB2UHau/cddshhzf40=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66i8Ytco4Yq/FMl6pIRZazz3CZlu8fO2OI6Pne0pvHU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2a/HgX+MjZxjXtSvHgF1yEpHMJBkl8Caee8XrJtn0WM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "frhBM662c4ZVG7mWP8K/HhRjd01lydW/cPcHnDjifqc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6k1T7Q1t668PBqv6fwpVnT1HWh7Am5LtbKvwPJKcpGU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UlJ5Edfusp8S/Pyhw6KTglIejmbr1HO0zUeHn/qFETA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jsxsB+1ECB3assUdoC333do9tYH+LglHmVSJHy4N8Hg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2nzIQxGYF7j3bGsIesECEOqhObKs/9ywknPHeJ3yges=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xJYKtuWrX90JrJVoYtnwP7Ce59XQGFYoalxpNfBXEH0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NLI5lriBTleGCELcHBtNnmnvwSRkHHaLOX4cKboMgTw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hUOQV0RmE5aJdJww1AR9rirJG4zOYPo+6cCkgn/BGvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "h4G2Of76AgxcUziBwCyH+ayMOpdBWzg4yFrTfehSC2c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VuamM75RzGfQpj2/Y1jSVuQLrhy6OAwlZxjuQLB/9Ss=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn9+hLq7hvw02xr9vrplOCDXKBTuFhfbX7d5v/l85Pg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fAiGqKyLZpGngBYFbtYUYt8LUrJ49vYafiboifTDjxs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BxRILymgfVJCczqjUIWXcfrfSgrrYkxTM5VTg0HkZLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CrFY/PzfPU2zsFkGLu/dI6mEeizZzCR+uYgjZBAHro0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "AEbrIuwvXLTtYgMjOqnGQ8y8axUn5Ukrn7UZRSyfQVw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ouWeVH3PEFg+dKWlXc6BmqirJOaVWjJbMzZbCsce4dA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+hd6xFB+EG+kVP7WH4uMd1CLaWMnt5xJRaY/Guuga9Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zmpGalfAOL3gmcUMJYcLYIRT/2VDO/1Dw4KdYZoNcng=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2PbHAoM/46J2UIZ/vyksKzmVVfxA7YUyIxWeL/N/vBk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7fD9x+zk5MVFesb59Klqiwwmve7P5ON/5COURXj5smE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tlrNQ4jaq051iaWonuv1sSrYhKkL1LtNZuHsvATha3s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fBodm28iClNpvlRyVq0dOdXQ08S7/N3aDwid+PdWvRo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "O+/nnRqT3Zv7yMMGug8GhKHaWy6u7BfRGtZoj0sdN1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5AZZ/RTMY4Photnm/cpXZr/HnFRi3eljacMsipkJLHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oFVyo/kgoMxBIk2VE52ySSimeyU+Gr0EfCwapXnTpKA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Z8v59DfcnviA0mzvnUk+URVO0UuqAWvtarEgJva/n1c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P64GOntZ+zBJEHkigoh9FSxSO+rJTqR20z5aiGQ9an4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xMbSuDPfWuO/Dm7wuVl06GnzG9uzTlJJX9vFy7boGlY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kXPB19mRClxdH2UsHwlttS6lLU2uHvzuZgZz7kC45jU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NDVjVYXAw4k0w4tFzvs7QDq39aaU3HQor4I2XMKKnCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uKw/+ErVfpTO1dGUfd3T/eWfZW3nUxXCdBGdjvHtZ88=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "av0uxEzWkizYWm0QUM/MN1hLibnxPvCWJKwjOV4yVQY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ERwUC47dvgOBzIsEESMIioLYbFOxOe8PtJTnmDkKuHM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2gseKlG5Le12fS/vj4eaED4lturF16kAgJ1TpW3HxEE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7Cvg0Y3j/5i2F1TeXxlMmU7xwif5dCmwkZAOrVC5K2Y=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Aggregate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Aggregate.json new file mode 100644 index 000000000..b078d1817 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Aggregate.json @@ -0,0 +1,584 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DecimalPrecision. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Correctness.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Correctness.json new file mode 100644 index 000000000..0859e702a --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Correctness.json @@ -0,0 +1,1647 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$lte": { + "$numberDecimal": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "0.0" + } + } + } + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "200.0" + } + } + } + }, + "result": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + }, + "$lte": { + "$numberDecimal": "200.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "-1" + } + } + }, + "result": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedDecimalPrecision": { + "$numberDecimal": "200.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 200, + "encryptedDecimalPrecision": { + "$numberDecimal": "200.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$lte": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "0.0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "200.0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + }, + "$lte": { + "$numberDecimal": "200.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberInt": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Delete.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Delete.json new file mode 100644 index 000000000..6e1ad90cd --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Delete.json @@ -0,0 +1,470 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DecimalPrecision. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.json new file mode 100644 index 000000000..1cfd19a1e --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.json @@ -0,0 +1,588 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DecimalPrecision. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimalPrecision": { + "$numberDecimal": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-InsertFind.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-InsertFind.json new file mode 100644 index 000000000..da7660972 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-InsertFind.json @@ -0,0 +1,571 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DecimalPrecision. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Update.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Update.json new file mode 100644 index 000000000..2d201948c --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DecimalPrecision-Update.json @@ -0,0 +1,588 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DecimalPrecision. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimalPrecision": { + "$numberDecimal": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Aggregate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Aggregate.json new file mode 100644 index 000000000..c188f1f5a --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Aggregate.json @@ -0,0 +1,1132 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Double. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2FIZh/9N+NeJEQwxYIX5ikQT85xJzulBNReXk8PnG/s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FWXI/yZ1M+2fIboeMCDMlp+I2NwPQDtoM/wWselOPYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uk26nvN/LdRLaBphiBgIZzT0sSpoO1z0RdDWRm/xrSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hiiYSH1KZovAULc7rlmEU74wCjzDR+mm6ZnsgvFQjMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hRzvMvWPX0sJme+wck67lwbKDFaWOa+Eyef+JSdc1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PSx5D+zqC9c295dguX4+EobT4IEzfffdfjzC8DWpB5Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QzfXQCVTjPQv2h21v95HYPq8uCsVJ2tPnjv79gAaM9M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XcGDO/dlTcEMLqwcm55UmOqK+KpBmbzZO1LIzX7GPaQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Lf+o4E7YB5ynzUPC6KTyW0lj6Cg9oLIu1Sdd1ODHctA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wAuVn02LAVo5Y+TUocvkoenFYWzpu38k0NmGZOsAjS4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yJGDtveLbbo/0HtCtiTSsvVI/0agg/U1bFaQ0yhK12o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KsEy0zgYcmkM+O/fWF9z3aJGIk22XCk+Aw96HB6JU68=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "p+AnMI5ZxdJMSIEJmXXya+FeH5yubmOdViwUO89j0Rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/jLix56jzeywBtNuGw55lCXyebQoSIhbful0hOKxKDY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fvDvSPomtJsl1S3+8/tzFCE8scHIdJY5hB9CdTEsoFo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oV5hOJzPXxfTuRdKIlF4uYEoMDuqH+G7/3qgndDr0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3ALwcvLj3VOfgD6OqXAO13h1ZkOv46R6+Oy6SUKh53I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gxaB9FJj0IM+InhvAjwWaex3UIZ9SAnDiUd5WHSY/l0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66NPvDygJzKJqddfNuDuNOpvGajjFRtvhkwfUkiYmXw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1dWcQIocRAcO9XnXYqbhl83jc0RgjQpsrWd8dC27trg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "npos0Uf1DT3ztSCjPVY9EImlRnTHB1KLrvmVSqBQ/8E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TEI9qBx/tK1l1H0v1scMG8Srmtwo5VxWHADPBSlWrXk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3wUN2ypQKoj+5ASkeIK9ycxhahVxyTmGopigoUAlyYs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o/oksSnUS+nIq6ozWTbB5bJh+NoaPj8deAA23uxiWCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KExYPruhA31e8xuSwvfUfDcyY/H2Va6taUd0k4yFgLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/x+dNfxdd/lkx8Z8VZVfoYl7LPoaZ/iKEzZXBrAtIJc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DE4cmjFLPqZlmRomO0qQiruUBtzoCe8ZdNRcfNH92pU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M6EKNcLPw/iojAChgYUSieaBYWcbsjKtB94SaHOr8vk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+qP49lDPeyhaduTvXJgtJEqHNEYANVu9Bg3Bxz7Td9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ruMrC2VIS+VKbJwCFb3bfkaLTju9nE+yPONV9s0M0Vo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EbjDlSB5JKnDKff4d8hOmaOwJ7B9Q6NQFisLj+DPC+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C/yYOTB94edyqAbiQNu8/H7FoG3yRRjHDkMykz4+Mv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CBxqrejG+qQQq2YTd6iP/06kiu2CxxzBFaZK3Ofb1CM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2ZOQ/fpho+AbDENWBZaln7wRoepIRdhyT648dr8O5cU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EghIgEPz01+myPgj8oid+PgncvobvC7vjvG3THEEQ0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92CysZYNF8riwAMhdrIPKxfODw9p07cKQy/Snn8XmVY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VO0LeTBQmsEf7sCHzTnZwUPNTqRZ49R8V5E9XnZ/5N4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "exs8BQMJq7U6ZXYgIizT7XN+X/hOmmn4YEuzev9zgSI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qHpS4k1I+gPniNp4CA8TY8lLN36vBYmgbKMFpbYMEqg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+7lWKCKAWFw6gPZdHE6E8KIfI14/fSvtWUmllb5WLi0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YiH/US0q6679hWblFDDKNqUjCgggoU8sUCssTIF1QbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YgwkKElEubNfvXL9hJxzqQUQtHiXN/OCGxNL1MUZZlM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hZFST4INZTTuhvJlGJeMwlUAK270UCOTCDeBAnN4a7g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "24I1Zw35AuGnK3CqJhbCwYb0IPuu5sCRrM5iyeITOLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vgD12JB4Q1S/kGPSQ1KOgp386KnG1GbM/5+60oRGcGw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+wNE+OL+CB9d4AUJdVxd56jUJCAXmmk9fapuB2TAc4g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uhQh1B2Pe4RkNw/kPEcgaLenuikKoRf1iyfZhpXdodc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eu8gjAUIp8ybO204AgeOq5v1neI1yljqy5v3I6lo1lM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7QG6oVbASBAjrnCPxzzUNnuFSFNlKhbuBafkF8pr7Is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PUS1xb2oHSDTdYltutoSSxBiJ1NjxH3l2kA4P1CZLEs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XPMh/JDC/O93gJJCwwgJDb8ssWZvRvezNmKmyn3nIfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jWz+KGwMk/GOvFAK2rOxF3OjxeZAWfmUQ1HGJ7icw4A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o7XbW68pc6flYigf3LW4WAGUWxpeqxaQLkHUhUR9RZ8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nqR+g60+5U0okbqJadSqGgnC+j1JcP8rwMcfzOs2ACI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hz43qVK95tSfbYFtaE/8fE97XMk1RiO8XpWjwZHB80o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "noZUWlZ8M6KXU5rkifyo8/duw5IL7/fXbJvT7bNmW9k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WONVHCuPSanXDRQQ/3tmyJ0Vq+Lu/4hRaMUf0g0kSuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UEaj6vQRoIghE8Movd8AGXhtwIOXlP4cBsECIUvE5Y8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "D3n2YcO8+PB4C8brDo7kxKjF9Y844rVkdRMLTgsQkrw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C+YA0G9KjxZVaWwOMuh/dcnHnHAlYnbFrRl0IEpmsY0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rUnmbmQanxrbFPYYrwyQ53x66OSt27yAvF+s48ezKDc=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Correctness.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Correctness.json new file mode 100644 index 000000000..3e298127d --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Correctness.json @@ -0,0 +1,1157 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gte": { + "$numberDouble": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$lte": { + "$numberDouble": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gte": { + "$numberDouble": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$lte": { + "$numberDouble": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberInt": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Delete.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Delete.json new file mode 100644 index 000000000..dc0ba435f --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Delete.json @@ -0,0 +1,726 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Double. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-FindOneAndUpdate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-FindOneAndUpdate.json new file mode 100644 index 000000000..4b96575e1 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-FindOneAndUpdate.json @@ -0,0 +1,1136 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Double. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HI88j1zrIsFoijIXKybr9mYubNV5uVeODyLHFH4Ueco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KhscCh+tt/pp8lxtKZQSPPUU94RvJYPKG/sjtzIa4Ws=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RISnuNrTTVNW5HnwCgQJ301pFw8DOcYrAMQIwVwjOkI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ra5zukLh2boua0Bh74qA+mtIoixGXlsNsxiJqHtqdTI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eqr0v+NNWXWszi9ni8qH58Q6gw5x737tJvH3lPaNHO4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d42QupriWIwGrFAquXNFi0ehEuidIbHLFZtg1Sm2nN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2azRVxaaTIJKcgY2FU012gcyP8Y05cRDpfUaMnCBaQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3nlgkM4K/AAcHesRYYdEu24UGetHodVnVfHzw4yxZBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hqy91FNmAAac2zUaPO6eWFkx0/37rOWGrwXN+fzL0tU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "akX+fmscSDSF9pB5MPj56iaJPtohr0hfXNk/OPWsGv8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZvUb10Q7cN4cNLktd5yNjqgtawsYnkbeVBZV6WuY/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "otCwtuKiY4hCyXvYzXvo10OcnzZppebo38KsAlq49QM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mty8EscckeT/dhMfrPFyDbLnmMOcYRUQ3mLK4KTu6V8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tnvgLLkJINO7csREYu4dEVe1ICrBeu7OP+HdfoX3M2E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kOefsHgEVhkJ17UuP7Dxogy6sAQbzf1SFPKCj6XRlrQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F+JQ79xavpaHdJzdhvwyHbzdZJLNHAymc/+67La3gao=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NCZ9zp5rDRceENuSgAfTLEyKg0YgmXAhK0B8WSj7+Pw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wL1CJ7cYR5slx8mHq++uMdjDfkt9037lQTUztEMF56M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "txefkzTMITZE+XvvRFZ7QcgwDT/7m8jNmxRk4QBaoZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jFunW3v1tSYMyZtQQD28eEy9qqDp4Kqo7gMN29N4bfQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QMO915KUiS3X3R1bU1YoafVM2s0NeHo3EjgTA9PnGwY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nwdKJEXdilzvb7494vbuDJ+y6SrfJahza1dYIsHIWVI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpWMX+T/VXXajFo0UbuYjtp0AEzBU0Y+lP+ih2EQ7mg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1lmzG0J1DhKDRhhq5y5Buygu4G8eV2X0t7kUY90EohM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SiKqpXqO0trwhFvBWK274hMklpCgMhNs/JY84yyn/NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7cPGPYCKPTay+ZR9Gx6oOueduOgaFrSuAXmNDpDHXdI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4THEYvAkjs2Fh7FIe5LC45P4i4N0L7ob67UOVbhp6Nk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+UGsChLLZR7iqnt8yq91OgmTgwiUKTJhFxY4NT0O6c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X1uYwBCsCg1H+PnKdwtBqXlt0zKEURi8bOM940GcPfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xYOgT5l7shlNXCwHlguovmDkcEnF8dXyYlTyYrgZ8GE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vFMTZqV8bh1+gcKzTkXweMddJlgdUnwX0DWzUUaMok4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4HI0y9FrtleZxZ7M6INdNhLelrQ2Rv/+ykWCBl+tMC8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpJ0bBE474OUkn1vUiLWumIBtYmwc7J5+LQU/nyeLQc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jQTPeXZvdxY/DjtPfYfKUArIDsf0E9MVFy2O26sv1ec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QLLto0ExR2ZYMGqlyaMZc/hXFFTlwmgtKbiVq/xJIeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yBJNviU1nchbGbhx6InXCVRXa90sEepz1EwbYuKXu2U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpEf0vHxrPu9gTJutNXSi2g/2Mc4WXFEN7yHonZEb7A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "E09kLFckMYwNuhggMxmPtwndyvIAx+Vl+b2CV6FP75s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "N+ue6/cLPb5NssmJCCeo18LlbKPz6r2z20AsnTKRvOo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yVQNZP8hhsvNGyDph2QP2qTNdXZTiIEVineKg+Qf33o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cSC9uI+9c5S8X+0G7amVyug1p0ZlgBsbEDYYyezBevQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1NpZGjoQzuQtekj80Rifxe9HbE08W07dfwxaFHaVn84=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5Ghuq/8l11Ug9Uf/RTwf9On3OxOwIXUcb9soiy4J7/w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0LWKaEty6ywxLFhDaAqulqfMnYc+tgPfH4apyEeKg80=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OwSthmCBtt6NIAoAh7aCbj82Yr/+9t8U7WuBQhFT3AQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iYiyg6/1isqbMdvFPIGucu3cNM4NAZNtJhHpGZ4eM+c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "waBgs8jWuGJPIF5zCRh6OmIyfK5GCBQgTMfmKSR2wyY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1Jdtbe2BKJXPU2G9ywOrlODZ/cNYEQlKzAW3aMe1Hy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaLEnNUS/2ySerBpb9dN/D31t+wYcKekwTfkwtni0Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bIVBrOhOvr6cL55Tr24+B+CC9MiG7U6K54aAr2IXXuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6Cdq5wroGu2TEFnekuT7LhOpd/K/+PcipIljcHU9QL4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K5l64vI4S/pLviLW6Pl0U3iQkI3ge0xg4RAHcEsyKJo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bzhuvZ0Ls22yIOX+Hz51eAHlSuDbWR/e0u4EhfdpHbc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Qv+fr6uD4o0bZRp69QJCFL6zvn3G82c7L+N1IFzj7H0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XAmISMbD3aEyQT+BQEphCKFNa0F0GDKFuhM9cGceKoQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4VLCokntMfm1AogpUnYGvhV7nllWSo3mS3hVESMy+hA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xiXNLj/CipEH63Vb5cidi8q9X47EF4f3HtJSOH7mfM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4XlCYfYBjI9XA5zOSgTiEBYcZsdwyXL+f5XtH2xUIOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "k6DfQy7ZYJIkEly2B5hjOZznL4NcgMkllZjJLb7yq7w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZzM6gwWesa3lxbZVZthpPFs2s3GV0RZREE2zOMhBRBo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "US+jeMeeOd7J0wR0efJtq2/18lcO8YFvhT4O3DeaonQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b6iSxiI1FM9SzxuG1bHqGA1i4+3GOi0/SPW00XB4L7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn3LsxAVkzIZKK9I6fi0Cctr0yjXOYgaQWMCoj4hLpM=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-InsertFind.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-InsertFind.json new file mode 100644 index 000000000..4827b6838 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-InsertFind.json @@ -0,0 +1,1123 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Double. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2FIZh/9N+NeJEQwxYIX5ikQT85xJzulBNReXk8PnG/s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I93Md7QNPGmEEGYU1+VVCqBPBEvXdqHPtTJtMOn06Yk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "GecBFQ1PemlECWZWCl7f74vmsL6eB6mzQ9n6tK6FYfs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QpjhZl+O1ORifgtCZuWAdcP6OKL7IZ2cA46v8FJcV28=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FWXI/yZ1M+2fIboeMCDMlp+I2NwPQDtoM/wWselOPYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uk26nvN/LdRLaBphiBgIZzT0sSpoO1z0RdDWRm/xrSA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hiiYSH1KZovAULc7rlmEU74wCjzDR+mm6ZnsgvFQjMw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hRzvMvWPX0sJme+wck67lwbKDFaWOa+Eyef+JSdc1s4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PSx5D+zqC9c295dguX4+EobT4IEzfffdfjzC8DWpB5Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QzfXQCVTjPQv2h21v95HYPq8uCsVJ2tPnjv79gAaM9M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XcGDO/dlTcEMLqwcm55UmOqK+KpBmbzZO1LIzX7GPaQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Lf+o4E7YB5ynzUPC6KTyW0lj6Cg9oLIu1Sdd1ODHctA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wAuVn02LAVo5Y+TUocvkoenFYWzpu38k0NmGZOsAjS4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yJGDtveLbbo/0HtCtiTSsvVI/0agg/U1bFaQ0yhK12o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KsEy0zgYcmkM+O/fWF9z3aJGIk22XCk+Aw96HB6JU68=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "p+AnMI5ZxdJMSIEJmXXya+FeH5yubmOdViwUO89j0Rc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/jLix56jzeywBtNuGw55lCXyebQoSIhbful0hOKxKDY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fvDvSPomtJsl1S3+8/tzFCE8scHIdJY5hB9CdTEsoFo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oV5hOJzPXxfTuRdKIlF4uYEoMDuqH+G7/3qgndDr0PM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3ALwcvLj3VOfgD6OqXAO13h1ZkOv46R6+Oy6SUKh53I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gxaB9FJj0IM+InhvAjwWaex3UIZ9SAnDiUd5WHSY/l0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "66NPvDygJzKJqddfNuDuNOpvGajjFRtvhkwfUkiYmXw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1dWcQIocRAcO9XnXYqbhl83jc0RgjQpsrWd8dC27trg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "npos0Uf1DT3ztSCjPVY9EImlRnTHB1KLrvmVSqBQ/8E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "TEI9qBx/tK1l1H0v1scMG8Srmtwo5VxWHADPBSlWrXk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3wUN2ypQKoj+5ASkeIK9ycxhahVxyTmGopigoUAlyYs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o/oksSnUS+nIq6ozWTbB5bJh+NoaPj8deAA23uxiWCk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KExYPruhA31e8xuSwvfUfDcyY/H2Va6taUd0k4yFgLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "/x+dNfxdd/lkx8Z8VZVfoYl7LPoaZ/iKEzZXBrAtIJc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DE4cmjFLPqZlmRomO0qQiruUBtzoCe8ZdNRcfNH92pU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M6EKNcLPw/iojAChgYUSieaBYWcbsjKtB94SaHOr8vk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+qP49lDPeyhaduTvXJgtJEqHNEYANVu9Bg3Bxz7Td9w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ruMrC2VIS+VKbJwCFb3bfkaLTju9nE+yPONV9s0M0Vo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EbjDlSB5JKnDKff4d8hOmaOwJ7B9Q6NQFisLj+DPC+0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C/yYOTB94edyqAbiQNu8/H7FoG3yRRjHDkMykz4+Mv0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CBxqrejG+qQQq2YTd6iP/06kiu2CxxzBFaZK3Ofb1CM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2ZOQ/fpho+AbDENWBZaln7wRoepIRdhyT648dr8O5cU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "EghIgEPz01+myPgj8oid+PgncvobvC7vjvG3THEEQ0M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "92CysZYNF8riwAMhdrIPKxfODw9p07cKQy/Snn8XmVY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VO0LeTBQmsEf7sCHzTnZwUPNTqRZ49R8V5E9XnZ/5N4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "exs8BQMJq7U6ZXYgIizT7XN+X/hOmmn4YEuzev9zgSI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qHpS4k1I+gPniNp4CA8TY8lLN36vBYmgbKMFpbYMEqg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+7lWKCKAWFw6gPZdHE6E8KIfI14/fSvtWUmllb5WLi0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YiH/US0q6679hWblFDDKNqUjCgggoU8sUCssTIF1QbU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YgwkKElEubNfvXL9hJxzqQUQtHiXN/OCGxNL1MUZZlM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hZFST4INZTTuhvJlGJeMwlUAK270UCOTCDeBAnN4a7g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "24I1Zw35AuGnK3CqJhbCwYb0IPuu5sCRrM5iyeITOLc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vgD12JB4Q1S/kGPSQ1KOgp386KnG1GbM/5+60oRGcGw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+wNE+OL+CB9d4AUJdVxd56jUJCAXmmk9fapuB2TAc4g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uhQh1B2Pe4RkNw/kPEcgaLenuikKoRf1iyfZhpXdodc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eu8gjAUIp8ybO204AgeOq5v1neI1yljqy5v3I6lo1lM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7QG6oVbASBAjrnCPxzzUNnuFSFNlKhbuBafkF8pr7Is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "PUS1xb2oHSDTdYltutoSSxBiJ1NjxH3l2kA4P1CZLEs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XPMh/JDC/O93gJJCwwgJDb8ssWZvRvezNmKmyn3nIfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jWz+KGwMk/GOvFAK2rOxF3OjxeZAWfmUQ1HGJ7icw4A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o7XbW68pc6flYigf3LW4WAGUWxpeqxaQLkHUhUR9RZ8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nqR+g60+5U0okbqJadSqGgnC+j1JcP8rwMcfzOs2ACI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Hz43qVK95tSfbYFtaE/8fE97XMk1RiO8XpWjwZHB80o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "noZUWlZ8M6KXU5rkifyo8/duw5IL7/fXbJvT7bNmW9k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WONVHCuPSanXDRQQ/3tmyJ0Vq+Lu/4hRaMUf0g0kSuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UEaj6vQRoIghE8Movd8AGXhtwIOXlP4cBsECIUvE5Y8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "D3n2YcO8+PB4C8brDo7kxKjF9Y844rVkdRMLTgsQkrw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "C+YA0G9KjxZVaWwOMuh/dcnHnHAlYnbFrRl0IEpmsY0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rUnmbmQanxrbFPYYrwyQ53x66OSt27yAvF+s48ezKDc=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Update.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Update.json new file mode 100644 index 000000000..c3284ad0f --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Double-Update.json @@ -0,0 +1,1140 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Double. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6YrBn2ofIw1b5ooakrLOwF41BWrps8OO0H9WH4/rtlE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "n+XAuFnP8Dov9TnhGFxNx0K/MnVM9WbJ7RouEu0ndO0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yRXojuVdn5GQtD97qYlaCL6cOLmZ7Cvcb3wFjkLUIdM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DuIkdRPITRs55I4SZmgomAHCIsDQmXRhW8+MOznkzSk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SsBk+Et1lTbU+QRPx+xyJ/jMkmfG+QCvQEpip2YYrzA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "crCIzOd8KhHvvUlX7M1v9bhvU4pLdTc+X2SuqoKU5Ek=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "YOWdCw4UrqnxkAaVjqmC4sKQDMVMHEpFGnlxpxdaU6E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "M3SShp81Ff8tQ632qKbv9MUcN6wjDaBReI0VXNu6Xh4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gzHlSPxpM0hT75kQvWFzGlOxKvDoiKQZOr19V6l2zXI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "s3JnppOGYw9SL2Q1kMAZs948v2F5PrpXjGei/HioDWs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cG6+3Gk/zEH68P/uuuwiAUVCuyJwa1LeV+t29FlPPAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dupdvR3AyJtM+g9NDKiaLVOtGca387JQp8w+V03m7Ig=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JqEQc5svj2jTvZ6LLA5ivE+kTb/0aRemSEmxk4G7Zrg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "szcXXXKnob+p3SoM4yED2R920LeJ7cVsclPMFTe4CeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o1QoGVXmuBdHwHm7aCtGMlMVKrjFdYvJXpoq6uhIAZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Jfm5wPlqqLCJRGQIqRq2NGmpn7s0Vrih2H3YAOoI2YU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zMHLb8ARbsYo8Ld05bqnGFf1Usha6EGb8QKwdSAyps0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yQdtq9lh5pugL7/i0Bj/PuZUUBUIzf+7wj1rl5y736w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wGWVZdO7qIuyDg/BqDgqjgoQ02h5YYgwXQB1oCin2NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "by9HMLj6NTEpgztZ5HSN6GxImkXPcaFINYDzgZY33X8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tWo0vbasi7bXmn/MsOx13VC1IsWtpx/nYp0uj4iMzdA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tQQpndUYd5O87lOtrGjH3wl9VsOK0ray7RMasL90sBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cQjXEDCMsOpKLLf+vlTgIHA+cbSJdzqhbSX9Wvh95aA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7yMpU48IxK9SzP2cx3VnTownGEwFmeFofuuFT97SuuY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kSOx1kz0CmBgzKQHZlo65ZUY1DIv9A99JRm+Us2y6Ew=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ubQpdPBe6/xvtr+AcXdfYLSvYCR4ot0tivehkCsupb4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xal+iCJ6FTefRQToyoNksc9NCZShyn04NDGi4IYrcoM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d7jU4iOK50xHxlkSifcxlZFCM46TSgQzoYivxG3HNLY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tJvl2nsBLBVzL3pp6sKWCL4UXeh3q/roYBJjSb74ve0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OIUCaKRvIx9t1w6Hxlz1IcQTdPNCfdRNwnnTm10W+X0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A9tvzsiElotOUVIB4CqfQp9mAwqvTM35YkmAR170aHA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lI8gpK7hpb7c9x4RQugsxMnQay5LZJmwslZdvMx/dcE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dNCzh40U0XvdKnSDi3HRQOWQftEsDVqc4uUvsVFGoq8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IP+iwEBWBwVVZIdpaMu8k5+soFCz+TZkYn3drKZ9grE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pnqyh6e0y5svHkJDShlN9CHV0WvMBE4QbtJpQw5ZCXc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "elEl42tbVDoRTLjAhZUFEtXiut4b3PVhg/1ZLZSQdtE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vHuu2FxwclMHqyE6JBYbTYgbEkB0dqb/JuaxsvfwsmY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xTf7NCe3Gf8QpE78HR5OknlLTKfs9J+RN9UZpH6fnso=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XiWSasRnJAulGR6+LCVD3mwRObXylqYWR9jvpywq12c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MZMxEQ5ikx0PG1YFIExv0UnTZogsvgeOEZTpzvBDn4w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yZMyMZBDrWbAhvnic7vvIYhmO9m5H2iuv0c8KNZrBzY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xxM14hTPY5j0vvcK2C7YAEjzdsfUTFHozHC0hEo1bxI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+01rqR1xVwkpGXcstbk1ItJqFVjH6Q8MGxEN3Cm9Y1A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xOpLV0Z2VTRJ3iWtnWZcsyjXubTIkYWo31cO+HV1o1k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BWUOLqgLBqc5NwxVlSV5H3KFQPXbCp7mdo+jF+8cJqY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fuQb1S6xZDGlrEbK+kI23aL53PP1PVNwqICnZNt9Yzg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfscnoibFttahLdPVC4Ee+47ewGFKpDSU7M6HX19bKE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rpSW2awybNVeKtat91VFxqbINoTfNhPfQAu+d73Xtf8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9M/CP9ccOIIj2LLFmE0GFDO0Ban2wsNalEXfM6+h+1s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrEMG49l1ye4MhXs5ZS9tz8P6h+hDvthIg/2wW9ne1Q=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ImNhbfeyfH8qIEeA5ic0s3dAQBdzzTBS+CPsNih9vZ0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dWP33YDSn04UKJN2ogh2Rui0iW/0q2y18OCDRVcfyoo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "lYv0isAtfGh6H9tdp3cp2eHU7q2J+uk7QrgcxtK3w7Y=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VGMoamB/+7zTOYcY/pqJc96xlv2PdW4hwsIAEIslTDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yNeBWMF7BnD9wVwz2PgJsvWr77QiVvvWUvJF0+fqBug=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SfpvObJ+tJBXSvqeN7vlOfmhYign635lciYAJIjUtY8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dsen4NqjzVGjpjufiTMs3+gqeD09EbnuogPgxrJECwg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "pxCWVM3sn19NsFEpgHbgLa+PmYlhN3mMiP0Wk8kJhYw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q11KNvJszjYIB9n9HcC+N4uz11a3eRj1L3BH9scKMDQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "A1PmkgcEToWh1JiVWE6mI5jUu7poxWWuCUt/cgRUUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "qJo3Hu4PJeanL7XEaWXO/n3YsodhZyd+MJOOmB9Kpd8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "BkBKLO8URFscfRY9Bav/1+L9mLohDgNr/MkZtGiraIs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rZq5WA3Hx3xthOyHAJXK//f8pE2qbz7YKu3TIMp9GFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X07a/Lm80p5xd4RFs1dNmw+90tmPDPdGiAKVZkxd4zY=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "HI88j1zrIsFoijIXKybr9mYubNV5uVeODyLHFH4Ueco=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wXVD/HSbBljko0jJcaxJ1nrzs2+pchLQqYR3vywS8SU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "KhscCh+tt/pp8lxtKZQSPPUU94RvJYPKG/sjtzIa4Ws=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RISnuNrTTVNW5HnwCgQJ301pFw8DOcYrAMQIwVwjOkI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ra5zukLh2boua0Bh74qA+mtIoixGXlsNsxiJqHtqdTI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eqr0v+NNWXWszi9ni8qH58Q6gw5x737tJvH3lPaNHO4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d42QupriWIwGrFAquXNFi0ehEuidIbHLFZtg1Sm2nN8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "2azRVxaaTIJKcgY2FU012gcyP8Y05cRDpfUaMnCBaQU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "3nlgkM4K/AAcHesRYYdEu24UGetHodVnVfHzw4yxZBM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hqy91FNmAAac2zUaPO6eWFkx0/37rOWGrwXN+fzL0tU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "akX+fmscSDSF9pB5MPj56iaJPtohr0hfXNk/OPWsGv8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1ZvUb10Q7cN4cNLktd5yNjqgtawsYnkbeVBZV6WuY/I=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "otCwtuKiY4hCyXvYzXvo10OcnzZppebo38KsAlq49QM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Mty8EscckeT/dhMfrPFyDbLnmMOcYRUQ3mLK4KTu6V8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "tnvgLLkJINO7csREYu4dEVe1ICrBeu7OP+HdfoX3M2E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kOefsHgEVhkJ17UuP7Dxogy6sAQbzf1SFPKCj6XRlrQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F+JQ79xavpaHdJzdhvwyHbzdZJLNHAymc/+67La3gao=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "NCZ9zp5rDRceENuSgAfTLEyKg0YgmXAhK0B8WSj7+Pw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wL1CJ7cYR5slx8mHq++uMdjDfkt9037lQTUztEMF56M=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "txefkzTMITZE+XvvRFZ7QcgwDT/7m8jNmxRk4QBaoZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jFunW3v1tSYMyZtQQD28eEy9qqDp4Kqo7gMN29N4bfQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QMO915KUiS3X3R1bU1YoafVM2s0NeHo3EjgTA9PnGwY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nwdKJEXdilzvb7494vbuDJ+y6SrfJahza1dYIsHIWVI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vpWMX+T/VXXajFo0UbuYjtp0AEzBU0Y+lP+ih2EQ7mg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1lmzG0J1DhKDRhhq5y5Buygu4G8eV2X0t7kUY90EohM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SiKqpXqO0trwhFvBWK274hMklpCgMhNs/JY84yyn/NE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7cPGPYCKPTay+ZR9Gx6oOueduOgaFrSuAXmNDpDHXdI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4THEYvAkjs2Fh7FIe5LC45P4i4N0L7ob67UOVbhp6Nk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "B+UGsChLLZR7iqnt8yq91OgmTgwiUKTJhFxY4NT0O6c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X1uYwBCsCg1H+PnKdwtBqXlt0zKEURi8bOM940GcPfk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xYOgT5l7shlNXCwHlguovmDkcEnF8dXyYlTyYrgZ8GE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vFMTZqV8bh1+gcKzTkXweMddJlgdUnwX0DWzUUaMok4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4HI0y9FrtleZxZ7M6INdNhLelrQ2Rv/+ykWCBl+tMC8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpJ0bBE474OUkn1vUiLWumIBtYmwc7J5+LQU/nyeLQc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jQTPeXZvdxY/DjtPfYfKUArIDsf0E9MVFy2O26sv1ec=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "QLLto0ExR2ZYMGqlyaMZc/hXFFTlwmgtKbiVq/xJIeI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yBJNviU1nchbGbhx6InXCVRXa90sEepz1EwbYuKXu2U=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jpEf0vHxrPu9gTJutNXSi2g/2Mc4WXFEN7yHonZEb7A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "E09kLFckMYwNuhggMxmPtwndyvIAx+Vl+b2CV6FP75s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "N+ue6/cLPb5NssmJCCeo18LlbKPz6r2z20AsnTKRvOo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "yVQNZP8hhsvNGyDph2QP2qTNdXZTiIEVineKg+Qf33o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cSC9uI+9c5S8X+0G7amVyug1p0ZlgBsbEDYYyezBevQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1NpZGjoQzuQtekj80Rifxe9HbE08W07dfwxaFHaVn84=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "5Ghuq/8l11Ug9Uf/RTwf9On3OxOwIXUcb9soiy4J7/w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0LWKaEty6ywxLFhDaAqulqfMnYc+tgPfH4apyEeKg80=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OwSthmCBtt6NIAoAh7aCbj82Yr/+9t8U7WuBQhFT3AQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "iYiyg6/1isqbMdvFPIGucu3cNM4NAZNtJhHpGZ4eM+c=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "waBgs8jWuGJPIF5zCRh6OmIyfK5GCBQgTMfmKSR2wyY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1Jdtbe2BKJXPU2G9ywOrlODZ/cNYEQlKzAW3aMe1Hy4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xaLEnNUS/2ySerBpb9dN/D31t+wYcKekwTfkwtni0Mc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bIVBrOhOvr6cL55Tr24+B+CC9MiG7U6K54aAr2IXXuw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6Cdq5wroGu2TEFnekuT7LhOpd/K/+PcipIljcHU9QL4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K5l64vI4S/pLviLW6Pl0U3iQkI3ge0xg4RAHcEsyKJo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "bzhuvZ0Ls22yIOX+Hz51eAHlSuDbWR/e0u4EhfdpHbc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Qv+fr6uD4o0bZRp69QJCFL6zvn3G82c7L+N1IFzj7H0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "XAmISMbD3aEyQT+BQEphCKFNa0F0GDKFuhM9cGceKoQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4VLCokntMfm1AogpUnYGvhV7nllWSo3mS3hVESMy+hA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "xiXNLj/CipEH63Vb5cidi8q9X47EF4f3HtJSOH7mfM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "4XlCYfYBjI9XA5zOSgTiEBYcZsdwyXL+f5XtH2xUIOc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "k6DfQy7ZYJIkEly2B5hjOZznL4NcgMkllZjJLb7yq7w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZzM6gwWesa3lxbZVZthpPFs2s3GV0RZREE2zOMhBRBo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "US+jeMeeOd7J0wR0efJtq2/18lcO8YFvhT4O3DeaonQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b6iSxiI1FM9SzxuG1bHqGA1i4+3GOi0/SPW00XB4L7o=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kn3LsxAVkzIZKK9I6fi0Cctr0yjXOYgaQWMCoj4hLpM=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Aggregate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Aggregate.json new file mode 100644 index 000000000..a2c1f3b75 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Aggregate.json @@ -0,0 +1,580 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DoublePrecision. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Correctness.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Correctness.json new file mode 100644 index 000000000..d0c0601ce --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Correctness.json @@ -0,0 +1,1647 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$lte": { + "$numberDouble": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "0.0" + } + } + } + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "200.0" + } + } + } + }, + "result": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + }, + "$lte": { + "$numberDouble": "200.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "-1" + } + } + }, + "result": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedDoublePrecision": { + "$numberDouble": "200.0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 200, + "encryptedDoublePrecision": { + "$numberDouble": "200.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$lte": { + "$numberDouble": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "0.0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "200.0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + }, + "$lte": { + "$numberDouble": "200.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberInt": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Delete.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Delete.json new file mode 100644 index 000000000..a617442ee --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Delete.json @@ -0,0 +1,468 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DoublePrecision. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-FindOneAndUpdate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-FindOneAndUpdate.json new file mode 100644 index 000000000..5565fb179 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-FindOneAndUpdate.json @@ -0,0 +1,584 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DoublePrecision. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDoublePrecision": { + "$numberDouble": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-InsertFind.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-InsertFind.json new file mode 100644 index 000000000..a1d8c1785 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-InsertFind.json @@ -0,0 +1,571 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DoublePrecision. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "mVZb+Ra0EYjQ4Zrh9X//E2T8MRj7NMqm5GUJXhRrBEI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "MgwakFvPyBlwqFTbhWUF79URJQWFoJTGotlEVSPPUsQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "DyBERpMSD5lEM5Nhpcn4WGgxgn/mkUVJp+PYSLX5jsE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "I43iazc0xj1WVbYB/V+uTL/tughN1bBlxh1iypBnNsA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wjOBa/ATMuOywFmuPgC0GF/oeLqu0Z7eK5udzkTPbis=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "gRQVwiR+m+0Vg8ZDXqrQQcVnTyobwCXNaA4BCJVXtMc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WUZ6huwx0ZbLb0R00uiC9FOJzsUocUN8qE5+YRenkvQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7s79aKEuPgQcS/YPOOVcYNZvHIo7FFsWtFCrnDKXefA=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Update.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Update.json new file mode 100644 index 000000000..6ea99242b --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-DoublePrecision-Update.json @@ -0,0 +1,588 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range DoublePrecision. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDoublePrecision": { + "$numberDouble": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Dri0CXmL78L2DOgk9w0DwxHOMGMzih7m6l59vgy+WWo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "b7d8mRzD1kI1tdc7uNL+YAUonJ6pODLsRLkArfEKSkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Xg8C1/A0KJaXOw4i+26Rv03/CydaaunOzXh0CIT+gn8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "UoKUDw2wJYToUCcFaIs03YQSTksYR0MIOTJllwODqKc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c/5cwAT0C5jber2xlJnWD3a5tVDy0nRtr5HG02hoFOY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wSUrRXavAGaajNeqC5mEUH1K67oYl5Wy9RNIzKjwLAM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6vrp4wWDtHEgHWR99I70WVDzevg1Fk/Pw5U8gUDa0OU=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "V6knyt7Zq2CG3++l75UtBx2m32iGAPjHiAe439Bf02w=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "0OKSXELxPP85SBVwDGf3LtMEQCJ8TTkFUl/+6jlkdb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uEw0lpQtBppR3vqV9j9+NQRSBF1BzZukb8c9IhyWvxc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zVhZ7Q59O087ji49oMJvBIgeir2oqvUpnh4p53GcTow=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "dowrzKs+qJhRMZyKDbhjXbuX43FbmUKOaw9I8YlOZDw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ep5B6cska6THLIF7Mn3tn3RvV9EiwLSt0eZM/CLRUDc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "URNp/YmmDh5wIZUfAzzgPyJeMNiVx9PMsz52DZRujGY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "wlM4IAQhhKQEzoVqS8b1Ddd50GB95OFb9LnzOwyjCP4=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Aggregate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Aggregate.json new file mode 100644 index 000000000..b3b2826fa --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Aggregate.json @@ -0,0 +1,484 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Int. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Correctness.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Correctness.json new file mode 100644 index 000000000..4932223ba --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Correctness.json @@ -0,0 +1,1641 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "1" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$lt": { + "$numberInt": "1" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$lte": { + "$numberInt": "1" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$lt": { + "$numberInt": "0" + } + } + } + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "200" + } + } + } + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + }, + "$lt": { + "$numberInt": "2" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + }, + "$lte": { + "$numberInt": "200" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$in": [ + { + "$numberInt": "0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "-1" + } + } + }, + "result": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedInt": { + "$numberInt": "200" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 200, + "encryptedInt": { + "$numberInt": "200" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "1" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$lt": { + "$numberInt": "1" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$lte": { + "$numberInt": "1" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$lt": { + "$numberInt": "0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "200" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + }, + "$lt": { + "$numberInt": "2" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$numberInt": "0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$numberInt": "1" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + }, + "$lte": { + "$numberInt": "200" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$in": [ + { + "$numberInt": "0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberDouble": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberDouble": "0" + } + } + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Delete.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Delete.json new file mode 100644 index 000000000..03f816e4b --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Delete.json @@ -0,0 +1,414 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Int. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-FindOneAndUpdate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-FindOneAndUpdate.json new file mode 100644 index 000000000..d573f7b6a --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-FindOneAndUpdate.json @@ -0,0 +1,488 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Int. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + }, + "update": { + "$set": { + "encryptedInt": { + "$numberInt": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedInt": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-InsertFind.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-InsertFind.json new file mode 100644 index 000000000..04953663f --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-InsertFind.json @@ -0,0 +1,475 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Int. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Update.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Update.json new file mode 100644 index 000000000..4c7a3c278 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Int-Update.json @@ -0,0 +1,492 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Int. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + }, + "update": { + "$set": { + "encryptedInt": { + "$numberInt": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedInt": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Aggregate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Aggregate.json new file mode 100644 index 000000000..a7e77fd5c --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Aggregate.json @@ -0,0 +1,484 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Long. Aggregate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + } + } + ], + "cursor": {}, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Correctness.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Correctness.json new file mode 100644 index 000000000..365822c79 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Correctness.json @@ -0,0 +1,1641 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Find with $gt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "1" + } + } + } + }, + "result": [] + } + ] + }, + { + "description": "Find with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$lt": { + "$numberLong": "1" + } + } + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$lte": { + "$numberLong": "1" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$lt": { + "$numberLong": "0" + } + } + } + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "200" + } + } + } + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + }, + "$lt": { + "$numberLong": "2" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + }, + "$lte": { + "$numberLong": "200" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$in": [ + { + "$numberLong": "0" + } + ] + } + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "-1" + } + } + }, + "result": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedLong": { + "$numberLong": "200" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 200, + "encryptedLong": { + "$numberLong": "200" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "1" + } + } + } + } + ] + }, + "result": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$lt": { + "$numberLong": "1" + } + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$lte": { + "$numberLong": "1" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$lt": { + "$numberLong": "0" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "200" + } + } + } + } + ] + }, + "result": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + }, + "$lt": { + "$numberLong": "2" + } + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$numberLong": "0" + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$numberLong": "1" + } + } + } + ] + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + }, + "$lte": { + "$numberLong": "200" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$in": [ + { + "$numberLong": "0" + } + ] + } + } + } + ] + }, + "result": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberDouble": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gte": { + "$numberDouble": "0" + } + } + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Delete.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Delete.json new file mode 100644 index 000000000..17a01fe07 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Delete.json @@ -0,0 +1,414 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Long. Delete.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + }, + "result": { + "deletedCount": 1 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-FindOneAndUpdate.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-FindOneAndUpdate.json new file mode 100644 index 000000000..918d0dfee --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-FindOneAndUpdate.json @@ -0,0 +1,488 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Long. FindOneAndUpdate.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + }, + "update": { + "$set": { + "encryptedLong": { + "$numberLong": "2" + } + } + }, + "returnDocument": "Before" + }, + "result": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encryptedLong": { + "$$type": "binData" + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-InsertFind.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-InsertFind.json new file mode 100644 index 000000000..9fafd10d4 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-InsertFind.json @@ -0,0 +1,475 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Long. Insert and Find.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + }, + "result": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "bE1vqWj3KNyM7cCYUv/cnYm8BPaUL3eMp5syTHq6NF4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FA74j21GUEJb1DJBOpR9nVnjaDZnd8yAQNuaW9Qi26g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kJv//KVkbrobIBf+QeWC5jxn20mx/P0R1N6aCSMgKM8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "zB+Whi9IUUGxfLEe+lGuIzLX4LFbIhaIAm5lRk65QTc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ybO1QU3CgvhO8JgRXH+HxKszWcpl5aGDYYVa75fHa1g=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "X3Y3eSAbbMg//JgiHHiFpYOpV61t8kkDexI+CQyitH4=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McjV8xwTF3xI7863DYOBdyvIv6UpzThl6v9vBRk05bI=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Update.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Update.json new file mode 100644 index 000000000..20ac25bfa --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-Long-Update.json @@ -0,0 +1,492 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "FLE2 Range Long. Update.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + }, + "update": { + "$set": { + "encryptedLong": { + "$numberLong": "2" + } + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command_name": "update", + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DUkFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/CRD+pGLD53BZzWcCcYbuGLVEyjzXIx7b+ux/q2BXMAIAAAAACOC6mXEZL27P9hethZbtKYsTXKK+FpgQ9Axxmn9N/cCwVsACAAAAAA+MFEd8XfZSpbXKqqPC2L3TEFswkaG5Ff6aSgf8p+XVIAAzEAfQAAAAVkACAAAAAAtL3QIvnZqCF72eS6lKr8ilff7R6kiNklokiTuaU5wNsFcwAgAAAAAEtqr3/X731VB+VrbFcY8ZrJKRo2E0Fd+C8L0EMNcvcCBWwAIAAAAABNPhSriux8W8qbwnhCczE3IzlhNEnGDpUwTFDZSL+eYQADMgB9AAAABWQAIAAAAAB99ZW/7KwXKzl5M3XQsAJ3JbEef90IoxFYBArNiYzlgQVzACAAAAAAYO/qaw0+92HAryxOUG7iK6hnIy3OaUA9jIqtHdvcq8YFbAAgAAAAAHrUYj8A0hVgc6VklpDiljOnykrUSfEsjm56XO/bsfKdAAMzAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzQAfQAAAAVkACAAAAAAV22FGF7ZDwK/EYiGNMlm/QuT3saQdyJM/Fn+ZyQug1oFcwAgAAAAACo7GwCvbcs5UHQMgds9/1QMklEVdjZigpuOFGrDmmxtBWwAIAAAAADQbYYPxlCMMGe2MulbiurApFLoeJSMvTeDU3pyEA2jNwADNQB9AAAABWQAIAAAAADFspsMG7yHjKppyllon1KqAsTrHaZ6JzNqnSz8o6iTvwVzACAAAAAAeiA5pqVIQQ9s6UY/P8v5Jjkl3I7iFNeLDYehikrINrsFbAAgAAAAAFjBTzTpNxDEkA0vSRj0jCED9KDRlboMVyilKyDz5YR4AAM2AH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzcAfQAAAAVkACAAAAAAYfLwnoxK6XAGQrJFy8+TIJoq38ldBaO75h4zA4ZX5tQFcwAgAAAAAC2wk8UcJH5X5XGnDBYmel6srpBkzBhHtt3Jw1u5TSJ1BWwAIAAAAAA9/YU9eI3D7QbXKIw/3/gzWJ6MZrCYhG0j1wNKgRQp5wADOAB9AAAABWQAIAAAAADGvyrtKkIcaV17ynZA7b2k5Pz6OhvxdWNkDvDWJIja8wVzACAAAAAAOLypVKNxf/wR1G8OZjUUsTQzDYeNNhhITxGMSp7euS4FbAAgAAAAAA9EsxoV1B2DcQ1NJRwuxXnvVR+vkD0wbbDYEI/zFEnDAAM5AH0AAAAFZAAgAAAAAEocREw1L0g+roFUchJI2Yd0M0ME2bnErNUYnpyJP1SqBXMAIAAAAAAcE2/JK/8MoSeOchIuAkKh1X3ImoA7p8ujAZIfvIDo6QVsACAAAAAA+W0+zgLr85/PD7P9a94wk6MgNgrizx/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAAA==", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedLong": { + "$$type": "binData" + } + } + } + } + ], + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "5nRutVIyq7URVOVtbE4vM01APSIajAVnsShMwjBlzkM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "x7GR49EN0t3WXQDihkrbonK7qNIBYC87tpL/XEUyIYc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JfYUqWF+OoGjiYkRI4L5iPlF+T1Eleul7Fki22jp4Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q1RyGfIgsaQHoZFRw+DD28V26rN5hweApPLwExncvT8=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "L2PFeKGvLS6C+DLudR6fGlBq3ERPvjWvRyNRIA2HVb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CWxaNqL3iP1yCixDkcmf9bmW3E5VeN8TJkg1jJe528s=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6SV63Mf51Z6A6p2X3rCnJKCu6ku3Oeb45mBYbz+IoAo=", + "subType": "00" + } + } + ] + }, + { + "_id": 1, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "DLCAJs+W2PL2DV5YChCL6dYrQNr+j4p3L7xhVaub4ic=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hyDcE6QQjPrYJaIS/n7evEZFYcm31Tj89CpEYGF45cI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "F08nMDWDZc+DbWM7XCEJNNCEYyinRmrvGP7EWhmp4is=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cXH4688amcDc8kZOJq4UP8cE3R58Zl7e+Qo/1jyspps=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uURBxvTp3FBCVkd+LPqyuY7d6rMW6SGIJQEPY/wtkZI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jG3hax1L3RBp9t38vUt53FsBxgr/+Si/vVISpAylYpE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "kwtIW8MhH9Ky5xNjBx8gFA/SHh2YVphie7g5FGBzals=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "FHflwFuEMu4xX0ZApHi+pdlBH+oevAtXckCUb5Wv0xU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ty4cnzJdAlbQKnh7px3GEYjBnvO+jIOaKjoTRDtmh3M=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-WrongType.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-WrongType.json new file mode 100644 index 000000000..5a6e650ab --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Range-WrongType.json @@ -0,0 +1,159 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "rangePreview", + "contention": { + "$numberLong": "0" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Wrong type: Insert Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberDouble": "0" + } + } + }, + "result": { + "errorContains": "cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberDouble": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "result": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-Update.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-Update.json new file mode 100644 index 000000000..cb260edc0 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-Update.json @@ -0,0 +1,570 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1648914851981" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Update can query an FLE2 indexed field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "value123" + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedIndexed": "value123" + }, + "update": { + "$set": { + "foo": "bar" + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "foo": "bar" + } + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "update" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "foo": "bar", + "__safeContent__": [ + { + "$binary": { + "base64": "ThpoKfQ8AkOzkFfNC1+9PF0pY2nIzfXvRdxQgjkNbBw=", + "subType": "00" + } + } + ] + } + ] + } + } + }, + { + "description": "Update can modify an FLE2 indexed field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "value123" + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedIndexed": "value123" + }, + "update": { + "$set": { + "encryptedIndexed": "value456" + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "encryptedIndexed": "value456" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedIndexed": { + "$$type": "binData" + } + } + } + } + ], + "ordered": true, + "encryptionInformation": { + "type": 1, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "command_name": "update" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": { + "$eq": 1 + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "rhe7/w8Ob8Unl44rGr/moScx6m5VODQnscDhF4Nkn6g=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2v2-validatorAndPartialFieldExpression.json b/tests/SpecTests/client-side-encryption/tests/fle2v2-validatorAndPartialFieldExpression.json new file mode 100644 index 000000000..901c4dd84 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/fle2v2-validatorAndPartialFieldExpression.json @@ -0,0 +1,503 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "tests": [ + { + "description": "create with a validator on an unencrypted field is OK", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection", + "validator": { + "unencrypted_string": "foo" + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + } + ] + }, + { + "description": "create with a validator on an encrypted field is an error", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection", + "validator": { + "encryptedIndexed": "foo" + } + }, + "result": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + }, + { + "description": "collMod with a validator on an unencrypted field is OK", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "collMod": "encryptedCollection", + "validator": { + "unencrypted_string": "foo" + } + } + } + } + ] + }, + { + "description": "collMod with a validator on an encrypted field is an error", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "collMod": "encryptedCollection", + "validator": { + "encryptedIndexed": "foo" + } + } + }, + "result": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + }, + { + "description": "createIndexes with a partialFilterExpression on an unencrypted field is OK", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "name", + "key": { + "name": 1 + }, + "partialFilterExpression": { + "unencrypted_string": "foo" + } + } + ] + } + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection", + "index": "name" + } + } + ] + }, + { + "description": "createIndexes with a partialFilterExpression on an encrypted field is an error", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "name", + "key": { + "name": 1 + }, + "partialFilterExpression": { + "encryptedIndexed": "foo" + } + } + ] + } + }, + "result": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/gcpKMS.json b/tests/SpecTests/client-side-encryption/tests/gcpKMS.json new file mode 100644 index 000000000..c2c08b8a2 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/gcpKMS.json @@ -0,0 +1,226 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": { + "encrypted_string_aws": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_azure": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_gcp": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_local": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_kmip": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "dBHpr8aITfeBQ15grpbLpQ==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "CiQAIgLj0WyktnB4dfYHo5SLZ41K4ASQrjJUaSzl5vvVH0G12G0SiQEAjlV8XPlbnHDEDFbdTO4QIe8ER2/172U1ouLazG0ysDtFFIlSvWX5ZnZUrRMmp/R2aJkzLXEt/zf8Mn4Lfm+itnjgo5R9K4pmPNvvPKNZX5C16lrPT+aA+rd+zXFSmlMg3i5jnxvTdLHhg3G7Q/Uv1ZIJskKt95bzLoe0tUVzRWMYXLIEcohnQg==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1601574333107" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1601574333107" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyAltNames": [ + "altname", + "gcp_altname" + ] + } + ], + "tests": [ + { + "description": "Insert a document with auto encryption using GCP KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "gcp": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_gcp": "string0" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_gcp": { + "$binary": { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string_gcp": { + "$binary": { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/getMore.json b/tests/SpecTests/client-side-encryption/tests/getMore.json index cf2344222..94e788ef6 100644 --- a/tests/SpecTests/client-side-encryption/tests/getMore.json +++ b/tests/SpecTests/client-side-encryption/tests/getMore.json @@ -179,18 +179,6 @@ "command_name": "find" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -228,7 +216,10 @@ "command_started_event": { "command": { "getMore": { - "$$type": "long" + "$$type": [ + "int", + "long" + ] }, "collection": "default", "batchSize": 2 diff --git a/tests/SpecTests/client-side-encryption/tests/insert.json b/tests/SpecTests/client-side-encryption/tests/insert.json index 78fa8feba..cf2910fd7 100644 --- a/tests/SpecTests/client-side-encryption/tests/insert.json +++ b/tests/SpecTests/client-side-encryption/tests/insert.json @@ -131,18 +131,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -258,18 +246,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/keyAltName.json b/tests/SpecTests/client-side-encryption/tests/keyAltName.json index d062bed45..7f71b9dbe 100644 --- a/tests/SpecTests/client-side-encryption/tests/keyAltName.json +++ b/tests/SpecTests/client-side-encryption/tests/keyAltName.json @@ -131,18 +131,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/kmipKMS.json b/tests/SpecTests/client-side-encryption/tests/kmipKMS.json new file mode 100644 index 000000000..5749d21ab --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/kmipKMS.json @@ -0,0 +1,223 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": { + "encrypted_string_aws": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_azure": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_gcp": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_local": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_kmip": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "dBHpr8aITfeBQ15grpbLpQ==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "dBHpr8aITfeBQ15grpbLpQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "eUYDyB0HuWb+lQgUwO+6qJQyTTDTY2gp9FbemL7ZFo0pvr0x6rm6Ff9OVUTGH6HyMKipaeHdiIJU1dzsLwvqKvi7Beh+U4iaIWX/K0oEg1GOsJc0+Z/in8gNHbGUYLmycHViM3LES3kdt7FdFSUl5rEBHrM71yoNEXImz17QJWMGOuT4x6yoi2pvnaRJwfrI4DjpmnnTrDMac92jgZehbg==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "kmip", + "keyId": "1" + }, + "keyAltNames": [ + "altname", + "kmip_altname" + ] + } + ], + "tests": [ + { + "description": "Insert a document with auto encryption using KMIP KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "kmip": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_kmip": "string0" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "dBHpr8aITfeBQ15grpbLpQ==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_kmip": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string_kmip": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/localKMS.json b/tests/SpecTests/client-side-encryption/tests/localKMS.json index e4d25309c..67c4ba130 100644 --- a/tests/SpecTests/client-side-encryption/tests/localKMS.json +++ b/tests/SpecTests/client-side-encryption/tests/localKMS.json @@ -114,18 +114,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/localSchema.json b/tests/SpecTests/client-side-encryption/tests/localSchema.json index 7071d6fef..4698520f6 100644 --- a/tests/SpecTests/client-side-encryption/tests/localSchema.json +++ b/tests/SpecTests/client-side-encryption/tests/localSchema.json @@ -136,18 +136,6 @@ } ], "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/maxWireVersion.json b/tests/SpecTests/client-side-encryption/tests/maxWireVersion.json index 144786290..f04f58dff 100644 --- a/tests/SpecTests/client-side-encryption/tests/maxWireVersion.json +++ b/tests/SpecTests/client-side-encryption/tests/maxWireVersion.json @@ -1,7 +1,7 @@ { "runOn": [ { - "maxServerVersion": "4.0" + "maxServerVersion": "4.0.99" } ], "database_name": "default", @@ -50,6 +50,9 @@ "autoEncryptOpts": { "kmsProviders": { "aws": {} + }, + "extraOptions": { + "mongocryptdBypassSpawn": true } } }, diff --git a/tests/SpecTests/client-side-encryption/tests/missingKey.json b/tests/SpecTests/client-side-encryption/tests/missingKey.json index ac8e8320b..275147bb7 100644 --- a/tests/SpecTests/client-side-encryption/tests/missingKey.json +++ b/tests/SpecTests/client-side-encryption/tests/missingKey.json @@ -140,18 +140,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "different" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/noSchema.json b/tests/SpecTests/client-side-encryption/tests/noSchema.json new file mode 100644 index 000000000..095434f88 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/noSchema.json @@ -0,0 +1,67 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "unencrypted", + "tests": [ + { + "description": "Insert on an unencrypted collection", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1 + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "unencrypted" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "insert": "unencrypted", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1 + } + ] + } + } + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/replaceOne.json b/tests/SpecTests/client-side-encryption/tests/replaceOne.json index 5cdb3d40f..975768681 100644 --- a/tests/SpecTests/client-side-encryption/tests/replaceOne.json +++ b/tests/SpecTests/client-side-encryption/tests/replaceOne.json @@ -148,18 +148,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/timeoutMS.json b/tests/SpecTests/client-side-encryption/tests/timeoutMS.json new file mode 100644 index 000000000..443aa0aa2 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/timeoutMS.json @@ -0,0 +1,200 @@ +{ + "runOn": [ + { + "minServerVersion": "4.4" + } + ], + "database_name": "cse-timeouts-db", + "collection_name": "cse-timeouts-coll", + "data": [], + "json_schema": { + "properties": { + "encrypted_w_altname": { + "encrypt": { + "keyId": "/altname", + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "random": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string_equivalent": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "status": 1, + "_id": { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "keyAltNames": [ + "altname", + "another_altname" + ] + } + ], + "tests": [ + { + "description": "timeoutMS applied to listCollections to get collection schema", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 60 + } + }, + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + }, + "timeoutMS": 50 + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + }, + "result": { + "isTimeoutError": true + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "cse-timeouts-coll" + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + }, + "command_name": "listCollections" + } + } + ] + }, + { + "description": "remaining timeoutMS applied to find to get keyvault data", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 3 + }, + "data": { + "failCommands": [ + "listCollections", + "find" + ], + "blockConnection": true, + "blockTimeMS": 20 + } + }, + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + }, + "timeoutMS": 50 + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + }, + "result": { + "isTimeoutError": true + } + } + ] + } + ] +} diff --git a/tests/SpecTests/client-side-encryption/tests/types.json b/tests/SpecTests/client-side-encryption/tests/types.json index 47e4c27a2..a6c6507e9 100644 --- a/tests/SpecTests/client-side-encryption/tests/types.json +++ b/tests/SpecTests/client-side-encryption/tests/types.json @@ -103,18 +103,6 @@ } ], "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -254,18 +242,6 @@ } ], "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -405,18 +381,6 @@ } ], "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -540,7 +504,7 @@ } }, "result": { - "errorContains": "Cannot use deterministic encryption for element of type: double" + "errorContains": "element of type: double" } } ] @@ -587,7 +551,7 @@ } }, "result": { - "errorContains": "Cannot use deterministic encryption for element of type: decimal" + "errorContains": "element of type: decimal" } } ] @@ -656,18 +620,6 @@ } ], "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -807,18 +759,6 @@ } ], "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -943,7 +883,7 @@ } }, "result": { - "errorContains": "Cannot use deterministic encryption for element of type: javascriptWithScope" + "errorContains": "element of type: javascriptWithScope" } } ] @@ -988,7 +928,7 @@ } }, "result": { - "errorContains": "Cannot use deterministic encryption for element of type: object" + "errorContains": "element of type: object" } } ] @@ -1057,18 +997,6 @@ } ], "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -1214,18 +1142,6 @@ } ], "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -1369,18 +1285,6 @@ } ], "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { @@ -1643,7 +1547,7 @@ } }, "result": { - "errorContains": "Cannot use deterministic encryption for element of type: array" + "errorContains": "element of type: array" } } ] @@ -1688,7 +1592,7 @@ } }, "result": { - "errorContains": "Cannot use deterministic encryption for element of type: bool" + "errorContains": "element of type: bool" } } ] diff --git a/tests/SpecTests/client-side-encryption/tests/updateMany.json b/tests/SpecTests/client-side-encryption/tests/updateMany.json index fd1f4d12b..823909044 100644 --- a/tests/SpecTests/client-side-encryption/tests/updateMany.json +++ b/tests/SpecTests/client-side-encryption/tests/updateMany.json @@ -164,18 +164,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/updateOne.json b/tests/SpecTests/client-side-encryption/tests/updateOne.json index bed763d72..23bada964 100644 --- a/tests/SpecTests/client-side-encryption/tests/updateOne.json +++ b/tests/SpecTests/client-side-encryption/tests/updateOne.json @@ -150,18 +150,6 @@ "command_name": "listCollections" } }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "datakeys" - }, - "$db": "keyvault" - }, - "command_name": "listCollections" - } - }, { "command_started_event": { "command": { diff --git a/tests/SpecTests/client-side-encryption/tests/validatorAndPartialFieldExpression.json b/tests/SpecTests/client-side-encryption/tests/validatorAndPartialFieldExpression.json new file mode 100644 index 000000000..e07137ce1 --- /dev/null +++ b/tests/SpecTests/client-side-encryption/tests/validatorAndPartialFieldExpression.json @@ -0,0 +1,642 @@ +{ + "runOn": [ + { + "minServerVersion": "6.0.0" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "tests": [ + { + "description": "create with a validator on an unencrypted field is OK", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "schemaMap": { + "default.encryptedCollection": { + "properties": { + "encrypted_w_altname": { + "encrypt": { + "keyId": "/altname", + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "random": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string_equivalent": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection", + "validator": { + "unencrypted_string": "foo" + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection" + } + } + ] + }, + { + "description": "create with a validator on an encrypted field is an error", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "schemaMap": { + "default.encryptedCollection": { + "properties": { + "encrypted_w_altname": { + "encrypt": { + "keyId": "/altname", + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "random": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string_equivalent": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection", + "validator": { + "encrypted_string": "foo" + } + }, + "result": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + }, + { + "description": "collMod with a validator on an unencrypted field is OK", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "schemaMap": { + "default.encryptedCollection": { + "properties": { + "encrypted_w_altname": { + "encrypt": { + "keyId": "/altname", + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "random": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string_equivalent": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "collMod": "encryptedCollection", + "validator": { + "unencrypted_string": "foo" + } + } + } + } + ] + }, + { + "description": "collMod with a validator on an encrypted field is an error", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "schemaMap": { + "default.encryptedCollection": { + "properties": { + "encrypted_w_altname": { + "encrypt": { + "keyId": "/altname", + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "random": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string_equivalent": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "collMod": "encryptedCollection", + "validator": { + "encrypted_string": "foo" + } + } + }, + "result": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + }, + { + "description": "createIndexes with a partialFilterExpression on an unencrypted field is OK", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "schemaMap": { + "default.encryptedCollection": { + "properties": { + "encrypted_w_altname": { + "encrypt": { + "keyId": "/altname", + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "random": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string_equivalent": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "name", + "key": { + "name": 1 + }, + "partialFilterExpression": { + "unencrypted_string": "foo" + } + } + ] + } + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "database": "default", + "collection": "encryptedCollection", + "index": "name" + } + } + ] + }, + { + "description": "createIndexes with a partialFilterExpression on an encrypted field is an error", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + }, + "schemaMap": { + "default.encryptedCollection": { + "properties": { + "encrypted_w_altname": { + "encrypt": { + "keyId": "/altname", + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "random": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string_equivalent": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + "operations": [ + { + "name": "dropCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "database", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "name", + "key": { + "name": 1 + }, + "partialFilterExpression": { + "encrypted_string": "foo" + } + } + ] + } + }, + "result": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + } + ] +} diff --git a/tests/SpecTests/command-monitoring/bulkWrite.json b/tests/SpecTests/command-monitoring/bulkWrite.json deleted file mode 100644 index ca5a9a105..000000000 --- a/tests/SpecTests/command-monitoring/bulkWrite.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "test", - "database_name": "command-monitoring-tests", - "tests": [ - { - "description": "A successful mixed bulk write", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 4, - "x": 44 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3 - }, - "update": { - "$set": { - "x": 333 - } - } - } - } - ] - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4, - "x": 44 - } - ], - "ordered": true - }, - "command_name": "insert", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 1 - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 3 - }, - "u": { - "$set": { - "x": 333 - } - } - } - ], - "ordered": true - }, - "command_name": "update", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 1 - }, - "command_name": "update" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/command-monitoring/command.json b/tests/SpecTests/command-monitoring/command.json deleted file mode 100644 index 7e1e347be..000000000 --- a/tests/SpecTests/command-monitoring/command.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "collection_name": "test", - "database_name": "command-monitoring-tests", - "tests": [ - { - "description": "A successful command", - "operation": { - "name": "count", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "count": "test", - "query": { - "_id": 1 - } - }, - "command_name": "count", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 1 - }, - "command_name": "count" - } - } - ] - }, - { - "description": "A failed command event", - "operation": { - "name": "count", - "arguments": { - "filter": { - "$or": true - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "count": "test", - "query": { - "$or": true - } - }, - "command_name": "count", - "database_name": "command-monitoring-tests" - } - }, - { - "command_failed_event": { - "command_name": "count" - } - } - ] - }, - { - "description": "A successful command with a non-primary read preference", - "operation": { - "name": "count", - "arguments": { - "filter": { - "_id": 1 - } - }, - "read_preference": { - "mode": "primaryPreferred" - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "count": "test", - "query": { - "_id": 1 - } - }, - "command_name": "count", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 1 - }, - "command_name": "count" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/command-monitoring/deleteMany.json b/tests/SpecTests/command-monitoring/deleteMany.json deleted file mode 100644 index 7cd396806..000000000 --- a/tests/SpecTests/command-monitoring/deleteMany.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "test", - "database_name": "command-monitoring-tests", - "tests": [ - { - "description": "A successful delete many", - "operation": { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "limit": 0 - } - ], - "ordered": true - }, - "command_name": "delete", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 2 - }, - "command_name": "delete" - } - } - ] - }, - { - "description": "A successful delete many command with write errors", - "operation": { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$nothing": 1 - } - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$nothing": 1 - } - }, - "limit": 0 - } - ], - "ordered": true - }, - "command_name": "delete", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": [ - { - "index": 0, - "code": 42, - "errmsg": "" - } - ] - }, - "command_name": "delete" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/command-monitoring/deleteOne.json b/tests/SpecTests/command-monitoring/deleteOne.json deleted file mode 100644 index 0971dfcf2..000000000 --- a/tests/SpecTests/command-monitoring/deleteOne.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "test", - "database_name": "command-monitoring-tests", - "tests": [ - { - "description": "A successful delete one", - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "limit": 1 - } - ], - "ordered": true - }, - "command_name": "delete", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 1 - }, - "command_name": "delete" - } - } - ] - }, - { - "description": "A successful delete one command with write errors", - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": { - "$nothing": 1 - } - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$nothing": 1 - } - }, - "limit": 1 - } - ], - "ordered": true - }, - "command_name": "delete", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": [ - { - "index": 0, - "code": 42, - "errmsg": "" - } - ] - }, - "command_name": "delete" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/command-monitoring/find.json b/tests/SpecTests/command-monitoring/find.json deleted file mode 100644 index 039c5fead..000000000 --- a/tests/SpecTests/command-monitoring/find.json +++ /dev/null @@ -1,560 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ], - "collection_name": "test", - "database_name": "command-monitoring-tests", - "namespace": "command-monitoring-tests.test", - "tests": [ - { - "description": "A successful find event with no options", - "operation": { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "filter": { - "_id": 1 - } - }, - "command_name": "find", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "cursor": { - "id": { - "$numberLong": "0" - }, - "ns": "command-monitoring-tests.test", - "firstBatch": [ - { - "_id": 1, - "x": 11 - } - ] - } - }, - "command_name": "find" - } - } - ] - }, - { - "description": "A successful find event with options", - "operation": { - "name": "find", - "read_preference": { - "mode": "primaryPreferred" - }, - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "sort": { - "_id": 1 - }, - "skip": { - "$numberLong": "2" - }, - "modifiers": { - "$comment": "test", - "$hint": { - "_id": 1 - }, - "$max": { - "_id": 6 - }, - "$maxTimeMS": 6000, - "$min": { - "_id": 0 - }, - "$returnKey": false, - "$showDiskLoc": false - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "filter": { - "_id": { - "$gt": 1 - } - }, - "sort": { - "_id": 1 - }, - "skip": { - "$numberLong": "2" - }, - "comment": "test", - "hint": { - "_id": 1 - }, - "max": { - "_id": 6 - }, - "maxTimeMS": 6000, - "min": { - "_id": 0 - }, - "returnKey": false, - "showRecordId": false - }, - "command_name": "find", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "cursor": { - "id": { - "$numberLong": "0" - }, - "ns": "command-monitoring-tests.test", - "firstBatch": [ - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ] - } - }, - "command_name": "find" - } - } - ] - }, - { - "description": "A successful find event with a getmore", - "operation": { - "name": "find", - "arguments": { - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": { - "$numberLong": "3" - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": { - "$numberLong": "3" - } - }, - "command_name": "find", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "cursor": { - "id": { - "$numberLong": "42" - }, - "ns": "command-monitoring-tests.test", - "firstBatch": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": { - "$numberLong": "3" - } - }, - "command_name": "getMore", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "cursor": { - "id": { - "$numberLong": "0" - }, - "ns": "command-monitoring-tests.test", - "nextBatch": [ - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ] - } - }, - "command_name": "getMore" - } - } - ] - }, - { - "description": "A successful find event with a getmore and killcursors", - "ignore_if_server_version_greater_than": "3.0", - "operation": { - "name": "find", - "arguments": { - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": { - "$numberLong": "3" - }, - "limit": { - "$numberLong": "4" - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": { - "$numberLong": "3" - }, - "limit": { - "$numberLong": "4" - } - }, - "command_name": "find", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "cursor": { - "id": { - "$numberLong": "42" - }, - "ns": "command-monitoring-tests.test", - "firstBatch": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": { - "$numberLong": "1" - } - }, - "command_name": "getMore", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "cursor": { - "id": { - "$numberLong": "42" - }, - "ns": "command-monitoring-tests.test", - "nextBatch": [ - { - "_id": 4, - "x": 44 - } - ] - } - }, - "command_name": "getMore" - } - }, - { - "command_started_event": { - "command": { - "killCursors": "test", - "cursors": [ - { - "$numberLong": "42" - } - ] - }, - "command_name": "killCursors", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "cursorsUnknown": [ - { - "$numberLong": "42" - } - ] - }, - "command_name": "killCursors" - } - } - ] - }, - { - "description": "A successful find event with a getmore and the server kills the cursor", - "ignore_if_server_version_less_than": "3.1", - "ignore_if_topology_type": [ - "sharded" - ], - "operation": { - "name": "find", - "arguments": { - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": { - "$numberLong": "3" - }, - "limit": { - "$numberLong": "4" - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": { - "$numberLong": "3" - }, - "limit": { - "$numberLong": "4" - } - }, - "command_name": "find", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "cursor": { - "id": { - "$numberLong": "42" - }, - "ns": "command-monitoring-tests.test", - "firstBatch": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": { - "$numberLong": "1" - } - }, - "command_name": "getMore", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "cursor": { - "id": { - "$numberLong": "0" - }, - "ns": "command-monitoring-tests.test", - "nextBatch": [ - { - "_id": 4, - "x": 44 - } - ] - } - }, - "command_name": "getMore" - } - } - ] - }, - { - "description": "A failed find event", - "operation": { - "name": "find", - "arguments": { - "filter": { - "$or": true - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "filter": { - "$or": true - } - }, - "command_name": "find", - "database_name": "command-monitoring-tests" - } - }, - { - "command_failed_event": { - "command_name": "find" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/command-monitoring/insertMany.json b/tests/SpecTests/command-monitoring/insertMany.json deleted file mode 100644 index 0becf928e..000000000 --- a/tests/SpecTests/command-monitoring/insertMany.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "collection_name": "test", - "database_name": "command-monitoring-tests", - "tests": [ - { - "description": "A successful insert many", - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - } - ] - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "ordered": true - }, - "command_name": "insert", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 1 - }, - "command_name": "insert" - } - } - ] - }, - { - "description": "A successful insert many command with write errors", - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1, - "x": 11 - } - ], - "ordered": true - }, - "command_name": "insert", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": [ - { - "index": 0, - "code": 42, - "errmsg": "" - } - ] - }, - "command_name": "insert" - } - } - ] - }, - { - "description": "A successful unordered insert many", - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "options": { - "ordered": false - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "ordered": false - }, - "command_name": "insert", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 1 - }, - "command_name": "insert" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/command-monitoring/insertOne.json b/tests/SpecTests/command-monitoring/insertOne.json deleted file mode 100644 index 877bca1a6..000000000 --- a/tests/SpecTests/command-monitoring/insertOne.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "collection_name": "test", - "database_name": "command-monitoring-tests", - "tests": [ - { - "description": "A successful insert one", - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "ordered": true - }, - "command_name": "insert", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 1 - }, - "command_name": "insert" - } - } - ] - }, - { - "description": "A successful insert one command with write errors", - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "x": 11 - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1, - "x": 11 - } - ], - "ordered": true - }, - "command_name": "insert", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": [ - { - "index": 0, - "code": 42, - "errmsg": "" - } - ] - }, - "command_name": "insert" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/command-monitoring/unacknowledgedBulkWrite.json b/tests/SpecTests/command-monitoring/unacknowledgedBulkWrite.json deleted file mode 100644 index ae116289e..000000000 --- a/tests/SpecTests/command-monitoring/unacknowledgedBulkWrite.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "collection_name": "test-unacknowledged-bulk-write", - "database_name": "command-monitoring-tests", - "tests": [ - { - "description": "A successful unordered bulk write with an unacknowledged write concern", - "comment": "On a 2.4 server, no GLE is sent and requires a client-side manufactured reply", - "operation": { - "name": "bulkWrite", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": "unorderedBulkWriteInsertW0", - "x": 44 - } - } - } - ], - "options": { - "ordered": false - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test-unacknowledged-bulk-write", - "documents": [ - { - "_id": "unorderedBulkWriteInsertW0", - "x": 44 - } - ], - "ordered": false, - "writeConcern": { - "w": 0 - } - }, - "command_name": "insert", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1 - }, - "command_name": "insert" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/command-monitoring/updateMany.json b/tests/SpecTests/command-monitoring/updateMany.json deleted file mode 100644 index d82792fc4..000000000 --- a/tests/SpecTests/command-monitoring/updateMany.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "test", - "database_name": "command-monitoring-tests", - "tests": [ - { - "description": "A successful update many", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true - } - ] - }, - "command_name": "update", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 2 - }, - "command_name": "update" - } - } - ] - }, - { - "description": "A successful update many command with write errors", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$nothing": { - "x": 1 - } - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$nothing": { - "x": 1 - } - }, - "multi": true - } - ] - }, - "command_name": "update", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": [ - { - "index": 0, - "code": 42, - "errmsg": "" - } - ] - }, - "command_name": "update" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/command-monitoring/updateOne.json b/tests/SpecTests/command-monitoring/updateOne.json deleted file mode 100644 index ba41dbb0c..000000000 --- a/tests/SpecTests/command-monitoring/updateOne.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "test", - "database_name": "command-monitoring-tests", - "tests": [ - { - "description": "A successful update one", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - } - } - ] - }, - "command_name": "update", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 1 - }, - "command_name": "update" - } - } - ] - }, - { - "description": "A successful update one with upsert when the upserted id is not an object id", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "ordered": true, - "updates": [ - { - "q": { - "_id": 4 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - ] - }, - "command_name": "update", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 1, - "upserted": [ - { - "index": 0, - "_id": 4 - } - ] - }, - "command_name": "update" - } - } - ] - }, - { - "description": "A successful update one command with write errors", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$nothing": { - "x": 1 - } - } - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$nothing": { - "x": 1 - } - } - } - ] - }, - "command_name": "update", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": [ - { - "index": 0, - "code": 42, - "errmsg": "" - } - ] - }, - "command_name": "update" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/crud/aggregate-merge.json b/tests/SpecTests/crud/aggregate-merge.json deleted file mode 100644 index c61736a0b..000000000 --- a/tests/SpecTests/crud/aggregate-merge.json +++ /dev/null @@ -1,415 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.11" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "test_aggregate_merge", - "tests": [ - { - "description": "Aggregate with $merge", - "operations": [ - { - "object": "collection", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test_aggregate_merge", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ] - } - } - } - ], - "outcome": { - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "Aggregate with $merge and batch size of 0", - "operations": [ - { - "object": "collection", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ], - "batchSize": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test_aggregate_merge", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ], - "cursor": {} - } - } - } - ], - "outcome": { - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "Aggregate with $merge and majority readConcern", - "operations": [ - { - "object": "collection", - "name": "aggregate", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test_aggregate_merge", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ], - "readConcern": { - "level": "majority" - } - } - } - } - ], - "outcome": { - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "Aggregate with $merge and local readConcern", - "operations": [ - { - "object": "collection", - "name": "aggregate", - "collectionOptions": { - "readConcern": { - "level": "local" - } - }, - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test_aggregate_merge", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ], - "readConcern": { - "level": "local" - } - } - } - } - ], - "outcome": { - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "Aggregate with $merge and available readConcern", - "operations": [ - { - "object": "collection", - "name": "aggregate", - "collectionOptions": { - "readConcern": { - "level": "available" - } - }, - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test_aggregate_merge", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ], - "readConcern": { - "level": "available" - } - } - } - } - ], - "outcome": { - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/aggregate-out-readConcern.json b/tests/SpecTests/crud/aggregate-out-readConcern.json deleted file mode 100644 index c39ee0e28..000000000 --- a/tests/SpecTests/crud/aggregate-out-readConcern.json +++ /dev/null @@ -1,385 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.0", - "topology": [ - "replicaset", - "sharded" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "test_aggregate_out_readconcern", - "tests": [ - { - "description": "readConcern majority with out stage", - "operations": [ - { - "object": "collection", - "name": "aggregate", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test_aggregate_out_readconcern", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "readConcern": { - "level": "majority" - } - } - } - } - ], - "outcome": { - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "readConcern local with out stage", - "operations": [ - { - "object": "collection", - "name": "aggregate", - "collectionOptions": { - "readConcern": { - "level": "local" - } - }, - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test_aggregate_out_readconcern", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "readConcern": { - "level": "local" - } - } - } - } - ], - "outcome": { - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "readConcern available with out stage", - "operations": [ - { - "object": "collection", - "name": "aggregate", - "collectionOptions": { - "readConcern": { - "level": "available" - } - }, - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test_aggregate_out_readconcern", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "readConcern": { - "level": "available" - } - } - } - } - ], - "outcome": { - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "readConcern linearizable with out stage", - "operations": [ - { - "object": "collection", - "name": "aggregate", - "collectionOptions": { - "readConcern": { - "level": "linearizable" - } - }, - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ] - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test_aggregate_out_readconcern", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "readConcern": { - "level": "linearizable" - } - } - } - } - ] - }, - { - "description": "invalid readConcern with out stage", - "operations": [ - { - "object": "collection", - "name": "aggregate", - "collectionOptions": { - "readConcern": { - "level": "!invalid123" - } - }, - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ] - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test_aggregate_out_readconcern", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "readConcern": { - "level": "!invalid123" - } - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/crud/bulkWrite-arrayFilters.json b/tests/SpecTests/crud/bulkWrite-arrayFilters.json deleted file mode 100644 index 2d3ce96de..000000000 --- a/tests/SpecTests/crud/bulkWrite-arrayFilters.json +++ /dev/null @@ -1,226 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.5.6" - } - ], - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ], - "collection_name": "test", - "database_name": "crud-tests", - "tests": [ - { - "description": "BulkWrite updateOne with arrayFilters", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - } - ], - "options": { - "ordered": true - } - }, - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": {}, - "u": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - ], - "ordered": true - }, - "command_name": "update", - "database_name": "crud-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 2 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - } - }, - { - "description": "BulkWrite updateMany with arrayFilters", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - } - ], - "options": { - "ordered": true - } - }, - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": {}, - "u": { - "$set": { - "y.$[i].b": 2 - } - }, - "multi": true, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - ], - "ordered": true - }, - "command_name": "update", - "database_name": "crud-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 2 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 2 - } - ] - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/bulkWrite-delete-hint-serverError.json b/tests/SpecTests/crud/bulkWrite-delete-hint-serverError.json deleted file mode 100644 index c68973b0f..000000000 --- a/tests/SpecTests/crud/bulkWrite-delete-hint-serverError.json +++ /dev/null @@ -1,209 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.3.3" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "collection_name": "BulkWrite_delete_hint", - "tests": [ - { - "description": "BulkWrite deleteOne with hints unsupported (server-side error)", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 2 - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "BulkWrite_delete_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": "_id_", - "limit": 1 - }, - { - "q": { - "_id": 2 - }, - "hint": { - "_id": 1 - }, - "limit": 1 - } - ], - "ordered": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "BulkWrite deleteMany with hints unsupported (server-side error)", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_" - } - }, - { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "BulkWrite_delete_hint", - "deletes": [ - { - "q": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_", - "limit": 0 - }, - { - "q": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - }, - "limit": 0 - } - ], - "ordered": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/bulkWrite-delete-hint.json b/tests/SpecTests/crud/bulkWrite-delete-hint.json deleted file mode 100644 index ece3238fc..000000000 --- a/tests/SpecTests/crud/bulkWrite-delete-hint.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "collection_name": "BulkWrite_delete_hint", - "tests": [ - { - "description": "BulkWrite deleteOne with hints", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 2 - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "result": { - "deletedCount": 2, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "BulkWrite_delete_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": "_id_", - "limit": 1 - }, - { - "q": { - "_id": 2 - }, - "hint": { - "_id": 1 - }, - "limit": 1 - } - ], - "ordered": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "BulkWrite deleteMany with hints", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_" - } - }, - { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "result": { - "deletedCount": 3, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "BulkWrite_delete_hint", - "deletes": [ - { - "q": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_", - "limit": 0 - }, - { - "q": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - }, - "limit": 0 - } - ], - "ordered": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/bulkWrite-update-hint-serverError.json b/tests/SpecTests/crud/bulkWrite-update-hint-serverError.json deleted file mode 100644 index e8b96fffe..000000000 --- a/tests/SpecTests/crud/bulkWrite-update-hint-serverError.json +++ /dev/null @@ -1,343 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.1.9" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "collection_name": "test_bulkwrite_update_hint", - "tests": [ - { - "description": "BulkWrite updateOne with update hints unsupported (server-side error)", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "BulkWrite updateMany with update hints unsupported (server-side error)", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": { - "$lt": 3 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": "_id_" - }, - { - "q": { - "_id": { - "$lt": 3 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "BulkWrite replaceOne with update hints unsupported (server-side error)", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 3 - }, - "replacement": { - "x": 333 - }, - "hint": "_id_" - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 444 - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": 3 - }, - "u": { - "x": 333 - }, - "hint": "_id_" - }, - { - "q": { - "_id": 4 - }, - "u": { - "x": 444 - }, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/bulkWrite-update-hint.json b/tests/SpecTests/crud/bulkWrite-update-hint.json deleted file mode 100644 index 15e169f76..000000000 --- a/tests/SpecTests/crud/bulkWrite-update-hint.json +++ /dev/null @@ -1,366 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.2.0" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "collection_name": "test_bulkwrite_update_hint", - "tests": [ - { - "description": "BulkWrite updateOne with update hints", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 13 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "BulkWrite updateMany with update hints", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 4, - "modifiedCount": 4, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": { - "$lt": 3 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": "_id_" - }, - { - "q": { - "_id": { - "$lt": 3 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 13 - }, - { - "_id": 2, - "x": 24 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "BulkWrite replaceOne with update hints", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 3 - }, - "replacement": { - "x": 333 - }, - "hint": "_id_" - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 444 - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": 3 - }, - "u": { - "x": 333 - }, - "hint": "_id_" - }, - { - "q": { - "_id": 4 - }, - "u": { - "x": 444 - }, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 333 - }, - { - "_id": 4, - "x": 444 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/deleteMany-hint-clientError.json b/tests/SpecTests/crud/deleteMany-hint-clientError.json deleted file mode 100644 index 3a0d02566..000000000 --- a/tests/SpecTests/crud/deleteMany-hint-clientError.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "3.3.99" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "DeleteMany_hint", - "tests": [ - { - "description": "DeleteMany with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "DeleteMany with hint document unsupported (client-side error)", - "operations": [ - { - "object": "collection", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/deleteMany-hint-serverError.json b/tests/SpecTests/crud/deleteMany-hint-serverError.json deleted file mode 100644 index 5829e86df..000000000 --- a/tests/SpecTests/crud/deleteMany-hint-serverError.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.3.3" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "DeleteMany_hint", - "tests": [ - { - "description": "DeleteMany with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteMany_hint", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_", - "limit": 0 - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "DeleteMany with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteMany_hint", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - }, - "limit": 0 - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/deleteMany-hint.json b/tests/SpecTests/crud/deleteMany-hint.json deleted file mode 100644 index 51ee38606..000000000 --- a/tests/SpecTests/crud/deleteMany-hint.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "DeleteMany_hint", - "tests": [ - { - "description": "DeleteMany with hint string", - "operations": [ - { - "object": "collection", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "result": { - "deletedCount": 2 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteMany_hint", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_", - "limit": 0 - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - }, - { - "description": "DeleteMany with hint document", - "operations": [ - { - "object": "collection", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "result": { - "deletedCount": 2 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteMany_hint", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - }, - "limit": 0 - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/deleteOne-hint-clientError.json b/tests/SpecTests/crud/deleteOne-hint-clientError.json deleted file mode 100644 index 97f8ec492..000000000 --- a/tests/SpecTests/crud/deleteOne-hint-clientError.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "3.3.99" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "DeleteOne_hint", - "tests": [ - { - "description": "DeleteOne with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne with hint document unsupported (client-side error)", - "operations": [ - { - "object": "collection", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/deleteOne-hint-serverError.json b/tests/SpecTests/crud/deleteOne-hint-serverError.json deleted file mode 100644 index 3cf9400a8..000000000 --- a/tests/SpecTests/crud/deleteOne-hint-serverError.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.3.3" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "DeleteOne_hint", - "tests": [ - { - "description": "DeleteOne with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteOne_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": "_id_", - "limit": 1 - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteOne_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": { - "_id": 1 - }, - "limit": 1 - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/deleteOne-hint.json b/tests/SpecTests/crud/deleteOne-hint.json deleted file mode 100644 index ec8e7715a..000000000 --- a/tests/SpecTests/crud/deleteOne-hint.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "DeleteOne_hint", - "tests": [ - { - "description": "DeleteOne with hint string", - "operations": [ - { - "object": "collection", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "result": { - "deletedCount": 1 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteOne_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": "_id_", - "limit": 1 - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "deleteOne with hint document", - "operations": [ - { - "object": "collection", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "result": { - "deletedCount": 1 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteOne_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": { - "_id": 1 - }, - "limit": 1 - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/find-allowdiskuse-clientError.json b/tests/SpecTests/crud/find-allowdiskuse-clientError.json deleted file mode 100644 index 5ea013966..000000000 --- a/tests/SpecTests/crud/find-allowdiskuse-clientError.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "3.0.99" - } - ], - "collection_name": "test_find_allowdiskuse_clienterror", - "tests": [ - { - "description": "Find fails when allowDiskUse true is specified against pre 3.2 server", - "operations": [ - { - "object": "collection", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": true - }, - "error": true - } - ], - "expectations": [] - }, - { - "description": "Find fails when allowDiskUse false is specified against pre 3.2 server", - "operations": [ - { - "object": "collection", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": false - }, - "error": true - } - ], - "expectations": [] - } - ] -} diff --git a/tests/SpecTests/crud/find-allowdiskuse-serverError.json b/tests/SpecTests/crud/find-allowdiskuse-serverError.json deleted file mode 100644 index 31aa50e95..000000000 --- a/tests/SpecTests/crud/find-allowdiskuse-serverError.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.2", - "maxServerVersion": "4.3.0" - } - ], - "collection_name": "test_find_allowdiskuse_servererror", - "tests": [ - { - "description": "Find fails when allowDiskUse true is specified against pre 4.4 server (server-side error)", - "operations": [ - { - "object": "collection", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": true - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test_find_allowdiskuse_servererror", - "filter": {}, - "allowDiskUse": true - } - } - } - ] - }, - { - "description": "Find fails when allowDiskUse false is specified against pre 4.4 server (server-side error)", - "operations": [ - { - "object": "collection", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": false - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test_find_allowdiskuse_servererror", - "filter": {}, - "allowDiskUse": false - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/crud/find-allowdiskuse.json b/tests/SpecTests/crud/find-allowdiskuse.json deleted file mode 100644 index 2df4dbc98..000000000 --- a/tests/SpecTests/crud/find-allowdiskuse.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1" - } - ], - "collection_name": "test_find_allowdiskuse", - "tests": [ - { - "description": "Find does not send allowDiskuse when value is not specified", - "operations": [ - { - "object": "collection", - "name": "find", - "arguments": { - "filter": {} - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test_find_allowdiskuse", - "allowDiskUse": null - } - } - } - ] - }, - { - "description": "Find sends allowDiskuse false when false is specified", - "operations": [ - { - "object": "collection", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": false - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test_find_allowdiskuse", - "allowDiskUse": false - } - } - } - ] - }, - { - "description": "Find sends allowDiskUse true when true is specified", - "operations": [ - { - "object": "collection", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": true - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test_find_allowdiskuse", - "allowDiskUse": true - } - } - } - ] - } - ] -} \ No newline at end of file diff --git a/tests/SpecTests/crud/findOneAndDelete-hint-clientError.json b/tests/SpecTests/crud/findOneAndDelete-hint-clientError.json deleted file mode 100644 index 262e78ce7..000000000 --- a/tests/SpecTests/crud/findOneAndDelete-hint-clientError.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.0.99" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "findOneAndDelete_hint", - "tests": [ - { - "description": "FindOneAndDelete with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndDelete with hint document", - "operations": [ - { - "object": "collection", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/findOneAndDelete-hint-serverError.json b/tests/SpecTests/crud/findOneAndDelete-hint-serverError.json deleted file mode 100644 index 9412b36f2..000000000 --- a/tests/SpecTests/crud/findOneAndDelete-hint-serverError.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.3.3" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "findOneAndDelete_hint", - "tests": [ - { - "description": "FindOneAndDelete with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndDelete_hint", - "query": { - "_id": 1 - }, - "hint": "_id_", - "remove": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndDelete with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndDelete_hint", - "query": { - "_id": 1 - }, - "hint": { - "_id": 1 - }, - "remove": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/findOneAndDelete-hint.json b/tests/SpecTests/crud/findOneAndDelete-hint.json deleted file mode 100644 index fe8dcfa4c..000000000 --- a/tests/SpecTests/crud/findOneAndDelete-hint.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "findOneAndDelete_hint", - "tests": [ - { - "description": "FindOneAndDelete with hint string", - "operations": [ - { - "object": "collection", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndDelete_hint", - "query": { - "_id": 1 - }, - "hint": "_id_", - "remove": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndDelete with hint document", - "operations": [ - { - "object": "collection", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndDelete_hint", - "query": { - "_id": 1 - }, - "hint": { - "_id": 1 - }, - "remove": true - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/findOneAndReplace-hint-clientError.json b/tests/SpecTests/crud/findOneAndReplace-hint-clientError.json deleted file mode 100644 index 08fd4b3ec..000000000 --- a/tests/SpecTests/crud/findOneAndReplace-hint-clientError.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.0.99" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "findOneAndReplace_hint", - "tests": [ - { - "description": "FindOneAndReplace with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndReplace with hint document unsupported (client-side error)", - "operations": [ - { - "object": "collection", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/findOneAndReplace-hint-serverError.json b/tests/SpecTests/crud/findOneAndReplace-hint-serverError.json deleted file mode 100644 index 6710e6a70..000000000 --- a/tests/SpecTests/crud/findOneAndReplace-hint-serverError.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.3.0" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "findOneAndReplace_hint", - "tests": [ - { - "description": "FindOneAndReplace with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndReplace_hint", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - }, - "hint": "_id_" - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndReplace with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndReplace_hint", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - }, - "hint": { - "_id": 1 - } - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/findOneAndReplace-hint.json b/tests/SpecTests/crud/findOneAndReplace-hint.json deleted file mode 100644 index 263fdf962..000000000 --- a/tests/SpecTests/crud/findOneAndReplace-hint.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "findOneAndReplace_hint", - "tests": [ - { - "description": "FindOneAndReplace with hint string", - "operations": [ - { - "object": "collection", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": "_id_" - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndReplace_hint", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - }, - "hint": "_id_" - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 33 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndReplace with hint document", - "operations": [ - { - "object": "collection", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndReplace_hint", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - }, - "hint": { - "_id": 1 - } - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 33 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/findOneAndUpdate-hint-clientError.json b/tests/SpecTests/crud/findOneAndUpdate-hint-clientError.json deleted file mode 100644 index 8cd5cddb5..000000000 --- a/tests/SpecTests/crud/findOneAndUpdate-hint-clientError.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.0.99" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "findOneAndUpdate_hint", - "tests": [ - { - "description": "FindOneAndUpdate with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate with hint document unsupported (client-side error)", - "operations": [ - { - "object": "collection", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/findOneAndUpdate-hint-serverError.json b/tests/SpecTests/crud/findOneAndUpdate-hint-serverError.json deleted file mode 100644 index 1f4b2bda8..000000000 --- a/tests/SpecTests/crud/findOneAndUpdate-hint-serverError.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.3.0" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "findOneAndUpdate_hint", - "tests": [ - { - "description": "FindOneAndUpdate with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndUpdate_hint", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndUpdate_hint", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/findOneAndUpdate-hint.json b/tests/SpecTests/crud/findOneAndUpdate-hint.json deleted file mode 100644 index 451eecc01..000000000 --- a/tests/SpecTests/crud/findOneAndUpdate-hint.json +++ /dev/null @@ -1,136 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "findOneAndUpdate_hint", - "tests": [ - { - "description": "FindOneAndUpdate with hint string", - "operations": [ - { - "object": "collection", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndUpdate_hint", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate with hint document", - "operations": [ - { - "object": "collection", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndUpdate_hint", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/replaceOne-hint.json b/tests/SpecTests/crud/replaceOne-hint.json deleted file mode 100644 index de4aa4d02..000000000 --- a/tests/SpecTests/crud/replaceOne-hint.json +++ /dev/null @@ -1,146 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.2.0" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "test_replaceone_hint", - "tests": [ - { - "description": "ReplaceOne with hint string", - "operations": [ - { - "object": "collection", - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": "_id_" - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_replaceone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "x": 111 - }, - "hint": "_id_" - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 111 - } - ] - } - } - }, - { - "description": "ReplaceOne with hint document", - "operations": [ - { - "object": "collection", - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": { - "_id": 1 - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_replaceone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "x": 111 - }, - "hint": { - "_id": 1 - } - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 111 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/replaceOne-validation.json b/tests/SpecTests/crud/replaceOne-validation.json deleted file mode 100644 index 2de4a6728..000000000 --- a/tests/SpecTests/crud/replaceOne-validation.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "ReplaceOne prohibits atomic modifiers", - "operations": [ - { - "object": "collection", - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "$set": { - "x": 22 - } - } - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-bulkWrite-delete-hint-clientError.json b/tests/SpecTests/crud/unacknowledged-bulkWrite-delete-hint-clientError.json deleted file mode 100644 index a92c5dfb8..000000000 --- a/tests/SpecTests/crud/unacknowledged-bulkWrite-delete-hint-clientError.json +++ /dev/null @@ -1,160 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.3.3" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "collection_name": "BulkWrite_delete_hint", - "tests": [ - { - "description": "Unacknowledged bulkWrite deleteOne with hints fails with client-side error on server < 4.4", - "operations": [ - { - "name": "bulkWrite", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 2 - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "Unacknowledged bulkWrite deleteMany with hints fails with client-side error on server < 4.4", - "operations": [ - { - "name": "bulkWrite", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "requests": [ - { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_" - } - }, - { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-bulkWrite-delete-hint.json b/tests/SpecTests/crud/unacknowledged-bulkWrite-delete-hint.json deleted file mode 100644 index b2147a27f..000000000 --- a/tests/SpecTests/crud/unacknowledged-bulkWrite-delete-hint.json +++ /dev/null @@ -1,174 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "collection_name": "BulkWrite_delete_hint", - "tests": [ - { - "description": "Unacknowledged bulkWrite deleteOne with hints succeeds on server >= 4.4", - "operations": [ - { - "name": "bulkWrite", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 2 - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "BulkWrite_delete_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": "_id_", - "limit": 1 - }, - { - "q": { - "_id": 2 - }, - "hint": { - "_id": 1 - }, - "limit": 1 - } - ], - "ordered": true - } - } - } - ], - "outcome": {} - }, - { - "description": "Unacknowledged bulkWrite deleteMany with hints succeeds on server >= 4.4", - "operations": [ - { - "name": "bulkWrite", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "requests": [ - { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_" - } - }, - { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "BulkWrite_delete_hint", - "deletes": [ - { - "q": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_", - "limit": 0 - }, - { - "q": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - }, - "limit": 0 - } - ], - "ordered": true - } - } - } - ], - "outcome": {} - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-bulkWrite-update-hint-clientError.json b/tests/SpecTests/crud/unacknowledged-bulkWrite-update-hint-clientError.json deleted file mode 100644 index 93c46f82e..000000000 --- a/tests/SpecTests/crud/unacknowledged-bulkWrite-update-hint-clientError.json +++ /dev/null @@ -1,250 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.1.9" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "collection_name": "Bulkwrite_update_hint", - "tests": [ - { - "description": "Unacknowledged bulkWrite updateOne with hints fails with client-side error on server < 4.2", - "operations": [ - { - "name": "bulkWrite", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "Unacknowledged bulkWrite updateMany with hints fails with client-side error on server < 4.2", - "operations": [ - { - "name": "bulkWrite", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "requests": [ - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "Unacknowledged bulkWrite replaceOne with hints unsupported (client-side error)", - "operations": [ - { - "name": "bulkWrite", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "requests": [ - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 3 - }, - "replacement": { - "x": 333 - }, - "hint": "_id_" - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 444 - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-bulkWrite-update-hint.json b/tests/SpecTests/crud/unacknowledged-bulkWrite-update-hint.json deleted file mode 100644 index eaba364e9..000000000 --- a/tests/SpecTests/crud/unacknowledged-bulkWrite-update-hint.json +++ /dev/null @@ -1,291 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.2.0" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "collection_name": "BulkWrite_update_hint", - "tests": [ - { - "description": "Unacknowledged bulkWrite updateOne with update hint succeeds on server >= 4.2", - "operations": [ - { - "name": "bulkWrite", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "BulkWrite_update_hint", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ], - "outcome": {} - }, - { - "description": "Unacknowledged bulkWrite updateMany with update hint succeeds on server >= 4.2", - "operations": [ - { - "name": "bulkWrite", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "requests": [ - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "BulkWrite_update_hint", - "updates": [ - { - "q": { - "_id": { - "$lt": 3 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": "_id_" - }, - { - "q": { - "_id": { - "$lt": 3 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ], - "outcome": {} - }, - { - "description": "Unacknowledged bulkWrite replaceOne with update hints", - "operations": [ - { - "name": "bulkWrite", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "requests": [ - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 3 - }, - "replacement": { - "x": 333 - }, - "hint": "_id_" - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 444 - }, - "hint": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "BulkWrite_update_hint", - "updates": [ - { - "q": { - "_id": 3 - }, - "u": { - "x": 333 - }, - "hint": "_id_" - }, - { - "q": { - "_id": 4 - }, - "u": { - "x": 444 - }, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ], - "outcome": {} - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-deleteMany-hint-clientError.json b/tests/SpecTests/crud/unacknowledged-deleteMany-hint-clientError.json deleted file mode 100644 index 903340bf0..000000000 --- a/tests/SpecTests/crud/unacknowledged-deleteMany-hint-clientError.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.3.3" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "DeleteMany_hint", - "tests": [ - { - "description": "Unacknowledged deleteMany with hint string fails with client-side error on server < 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "Unacknowledged deleteMany with hint document fails with client-side error on server < 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-deleteMany-hint.json b/tests/SpecTests/crud/unacknowledged-deleteMany-hint.json deleted file mode 100644 index 485fd6198..000000000 --- a/tests/SpecTests/crud/unacknowledged-deleteMany-hint.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "DeleteMany_hint", - "tests": [ - { - "description": "Unacknowledged deleteMany with hint string succeeds on server >= 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteMany_hint", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_", - "limit": 0 - } - ] - } - } - } - ], - "outcome": {} - }, - { - "description": "Unacknowledged deleteMany with hint document succeeds on server >= 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteMany_hint", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - }, - "limit": 0 - } - ] - } - } - } - ], - "outcome": {} - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-deleteOne-hint-clientError.json b/tests/SpecTests/crud/unacknowledged-deleteOne-hint-clientError.json deleted file mode 100644 index 11e0b883c..000000000 --- a/tests/SpecTests/crud/unacknowledged-deleteOne-hint-clientError.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.3.3" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "DeleteOne_hint", - "tests": [ - { - "description": "Unacknowledged deleteOne with hint string fails with client-side error on server < 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Unacknowledged deleteOne with hint document fails with client-side error on server < 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-deleteOne-hint.json b/tests/SpecTests/crud/unacknowledged-deleteOne-hint.json deleted file mode 100644 index bd9443d4f..000000000 --- a/tests/SpecTests/crud/unacknowledged-deleteOne-hint.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "DeleteOne_hint", - "tests": [ - { - "description": "Unacknowledged deleteOne with hint string succeeds on server >= 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteOne_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": "_id_", - "limit": 1 - } - ] - } - } - } - ], - "outcome": {} - }, - { - "description": "Unacknowledged deleteOne with hint document succeeds on server >= 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "DeleteOne_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": { - "_id": 1 - }, - "limit": 1 - } - ] - } - } - } - ], - "outcome": {} - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-findOneAndDelete-hint-clientError.json b/tests/SpecTests/crud/unacknowledged-findOneAndDelete-hint-clientError.json deleted file mode 100644 index 1c4423d3c..000000000 --- a/tests/SpecTests/crud/unacknowledged-findOneAndDelete-hint-clientError.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.3.3" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "findOneAndDelete_hint", - "tests": [ - { - "description": "Unacknowledged findOneAndDelete with hint string fails with client-side error on server < 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Unacknowledged findOneAndDelete with hint document fails with client-side error on server < 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-findOneAndDelete-hint.json b/tests/SpecTests/crud/unacknowledged-findOneAndDelete-hint.json deleted file mode 100644 index 9dce1ae9d..000000000 --- a/tests/SpecTests/crud/unacknowledged-findOneAndDelete-hint.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "findOneAndDelete_hint", - "tests": [ - { - "description": "Unacknowledged findOneAndDelete with hint string succeeds on server >= 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndDelete_hint", - "query": { - "_id": 1 - }, - "hint": "_id_", - "remove": true - } - } - } - ], - "outcome": {} - }, - { - "description": "Unacknowledged findOneAndDelete with hint document succeeds on server >= 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "findOneAndDelete_hint", - "query": { - "_id": 1 - }, - "hint": { - "_id": 1 - }, - "remove": true - } - } - } - ], - "outcome": {} - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-findOneAndReplace-hint-clientError.json b/tests/SpecTests/crud/unacknowledged-findOneAndReplace-hint-clientError.json deleted file mode 100644 index 0379e7946..000000000 --- a/tests/SpecTests/crud/unacknowledged-findOneAndReplace-hint-clientError.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.3.3" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "FindOneAndReplace_hint", - "tests": [ - { - "description": "Unacknowledged findOneAndReplace with hint string fails with client-side error on server < 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Unacknowledged findOneAndReplace with hint document fails with client-side error on server < 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-findOneAndReplace-hint.json b/tests/SpecTests/crud/unacknowledged-findOneAndReplace-hint.json deleted file mode 100644 index 7c3e55e04..000000000 --- a/tests/SpecTests/crud/unacknowledged-findOneAndReplace-hint.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "FindOneAndReplace_hint", - "tests": [ - { - "description": "Unacknowledged findOneAndReplace with hint string succeeds on server >= 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": "_id_" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "FindOneAndReplace_hint", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - }, - "hint": "_id_" - } - } - } - ], - "outcome": {} - }, - { - "description": "Unacknowledged findOneAndReplace with hint document succeeds on server >= 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": { - "_id": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "FindOneAndReplace_hint", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - } - } - } - } - ], - "outcome": {} - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-findOneAndUpdate-hint-clientError.json b/tests/SpecTests/crud/unacknowledged-findOneAndUpdate-hint-clientError.json deleted file mode 100644 index 14e1fb6da..000000000 --- a/tests/SpecTests/crud/unacknowledged-findOneAndUpdate-hint-clientError.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.3.3" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "FindOneAndUpdate_hint", - "tests": [ - { - "description": "Unacknowledged findOneAndUpdate with hint string fails with client-side error on server < 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Unacknowledged findOneAndUpdate with hint document fails with client-side error on server < 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-findOneAndUpdate-hint.json b/tests/SpecTests/crud/unacknowledged-findOneAndUpdate-hint.json deleted file mode 100644 index 383ca6ad6..000000000 --- a/tests/SpecTests/crud/unacknowledged-findOneAndUpdate-hint.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "FindOneAndUpdate_hint", - "tests": [ - { - "description": "Unacknowledged findOneAndUpdate with hint string succeeds on server >= 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "FindOneAndUpdate_hint", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - } - ], - "outcome": {} - }, - { - "description": "Unacknowledged findOneAndUpdate with hint document succeeds on server >= 4.4", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "FindOneAndUpdate_hint", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - } - ], - "outcome": {} - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-replaceOne-hint-clientError.json b/tests/SpecTests/crud/unacknowledged-replaceOne-hint-clientError.json deleted file mode 100644 index f442953f2..000000000 --- a/tests/SpecTests/crud/unacknowledged-replaceOne-hint-clientError.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.1.9" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "ReplaceOne_hint", - "tests": [ - { - "description": "Unacknowledged ReplaceOne with hint string fails with client-side error on server < 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Unacknowledged ReplaceOne with hint document fails with client-side error on server < 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-replaceOne-hint.json b/tests/SpecTests/crud/unacknowledged-replaceOne-hint.json deleted file mode 100644 index 1d896bfb5..000000000 --- a/tests/SpecTests/crud/unacknowledged-replaceOne-hint.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.2.0" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "Replaceone_hint", - "tests": [ - { - "description": "Unacknowledged replaceOne with hint string succeeds on server >= 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": "_id_" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "Replaceone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "x": 111 - }, - "hint": "_id_" - } - ] - } - } - } - ], - "outcome": {} - }, - { - "description": "Unacknowledged replaceOne with hint document succeeds on server >= 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": { - "_id": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "Replaceone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "x": 111 - }, - "hint": { - "_id": 1 - } - } - ] - } - } - } - ], - "outcome": {} - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-updateMany-hint-clientError.json b/tests/SpecTests/crud/unacknowledged-updateMany-hint-clientError.json deleted file mode 100644 index 9425d542e..000000000 --- a/tests/SpecTests/crud/unacknowledged-updateMany-hint-clientError.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.1.9" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "Updatemany_hint", - "tests": [ - { - "description": "Unacknowledged updateMany with hint string fails with client-side error on server < 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "Unacknowledged updateMany with hint document fails with client-side error on server < 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-updateMany-hint.json b/tests/SpecTests/crud/unacknowledged-updateMany-hint.json deleted file mode 100644 index d601a1b70..000000000 --- a/tests/SpecTests/crud/unacknowledged-updateMany-hint.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.2.0" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "UpdateMany_hint", - "tests": [ - { - "description": "Unacknowledged updateMany with hint string succeeds on server >= 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "UpdateMany_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": "_id_" - } - ] - } - } - } - ], - "outcome": {} - }, - { - "description": "Unacknowledged updateMany with hint document succeeds on server >= 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "UpdateMany_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": { - "_id": 1 - } - } - ] - } - } - } - ], - "outcome": {} - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-updateOne-hint-clientError.json b/tests/SpecTests/crud/unacknowledged-updateOne-hint-clientError.json deleted file mode 100644 index bce3add67..000000000 --- a/tests/SpecTests/crud/unacknowledged-updateOne-hint-clientError.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.1.9" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "Updateone_hint", - "tests": [ - { - "description": "Unacknowledged updateOne with hint string fails with client-side error on server < 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Unacknowledged updateOne with hint document fails with client-side error on server < 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": {}, - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/unacknowledged-updateOne-hint.json b/tests/SpecTests/crud/unacknowledged-updateOne-hint.json deleted file mode 100644 index ca2f57f62..000000000 --- a/tests/SpecTests/crud/unacknowledged-updateOne-hint.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.2.0" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "updateone_hint", - "tests": [ - { - "description": "Unacknowledged updateOne with hint string succeeds on server >= 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "updateone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - ] - } - } - } - ], - "outcome": {} - }, - { - "description": "Unacknowledged updateOne with hint document succeeds on server >= 4.2", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "updateone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - ] - } - } - } - ], - "outcome": {} - } - ] -} diff --git a/tests/SpecTests/crud/updateMany-hint-serverError.json b/tests/SpecTests/crud/updateMany-hint-serverError.json deleted file mode 100644 index 86f21246e..000000000 --- a/tests/SpecTests/crud/updateMany-hint-serverError.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.1.9" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "test_updatemany_hint", - "tests": [ - { - "description": "UpdateMany with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_updatemany_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": "_id_" - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "UpdateMany with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_updatemany_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": { - "_id": 1 - } - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/updateMany-hint.json b/tests/SpecTests/crud/updateMany-hint.json deleted file mode 100644 index 489348917..000000000 --- a/tests/SpecTests/crud/updateMany-hint.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.2.0" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection_name": "test_updatemany_hint", - "tests": [ - { - "description": "UpdateMany with hint string", - "operations": [ - { - "object": "collection", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_updatemany_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": "_id_" - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 34 - } - ] - } - } - }, - { - "description": "UpdateMany with hint document", - "operations": [ - { - "object": "collection", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_updatemany_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": { - "_id": 1 - } - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 34 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/updateMany-validation.json b/tests/SpecTests/crud/updateMany-validation.json deleted file mode 100644 index a85ccfa86..000000000 --- a/tests/SpecTests/crud/updateMany-validation.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "UpdateOne requires atomic modifiers", - "operations": [ - { - "object": "collection", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "x": 44 - } - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/updateOne-hint-clientError.json b/tests/SpecTests/crud/updateOne-hint-clientError.json deleted file mode 100644 index 82bfe368c..000000000 --- a/tests/SpecTests/crud/updateOne-hint-clientError.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "3.3.99" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "test_updateone_hint", - "tests": [ - { - "description": "UpdateOne with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne with hint document unsupported (client-side error)", - "operations": [ - { - "object": "collection", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/updateOne-hint-serverError.json b/tests/SpecTests/crud/updateOne-hint-serverError.json deleted file mode 100644 index 8e8037eb8..000000000 --- a/tests/SpecTests/crud/updateOne-hint-serverError.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.1.9" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "test_updateone_hint", - "tests": [ - { - "description": "UpdateOne with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_updateone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_updateone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/updateOne-hint.json b/tests/SpecTests/crud/updateOne-hint.json deleted file mode 100644 index 43f76da49..000000000 --- a/tests/SpecTests/crud/updateOne-hint.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.2.0" - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "test_updateone_hint", - "tests": [ - { - "description": "UpdateOne with hint string", - "operations": [ - { - "object": "collection", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_updateone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - } - ] - } - } - }, - { - "description": "UpdateOne with hint document", - "operations": [ - { - "object": "collection", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test_updateone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - ] - } - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/updateOne-validation.json b/tests/SpecTests/crud/updateOne-validation.json deleted file mode 100644 index 6c919f5ea..000000000 --- a/tests/SpecTests/crud/updateOne-validation.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "UpdateOne requires atomic modifiers", - "operations": [ - { - "object": "collection", - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "x": 22 - } - }, - "error": true - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/crud/updateWithPipelines.json b/tests/SpecTests/crud/updateWithPipelines.json deleted file mode 100644 index a310f2825..000000000 --- a/tests/SpecTests/crud/updateWithPipelines.json +++ /dev/null @@ -1,408 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.11" - } - ], - "data": [ - { - "_id": 1, - "x": 1, - "y": 1, - "t": { - "u": { - "v": 1 - } - } - }, - { - "_id": 2, - "x": 2, - "y": 1 - } - ], - "collection_name": "test", - "database_name": "crud-tests", - "tests": [ - { - "description": "UpdateOne using pipelines", - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceRoot": { - "newRoot": "$t" - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceRoot": { - "newRoot": "$t" - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - } - ] - }, - "command_name": "update", - "database_name": "crud-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "u": { - "v": 1 - }, - "foo": 1 - }, - { - "_id": 2, - "x": 2, - "y": 1 - } - ] - } - } - }, - { - "description": "UpdateMany using pipelines", - "operations": [ - { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - }, - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": {}, - "u": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ], - "multi": true - } - ] - }, - "command_name": "update", - "database_name": "crud-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 1, - "foo": 1 - }, - { - "_id": 2, - "x": 2, - "foo": 1 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate using pipelines", - "operations": [ - { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "update": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - }, - "command_name": "findAndModify", - "database_name": "crud-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 1, - "foo": 1 - }, - { - "_id": 2, - "x": 2, - "y": 1 - } - ] - } - } - }, - { - "description": "UpdateOne in bulk write using pipelines", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceRoot": { - "newRoot": "$t" - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - } - } - ] - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceRoot": { - "newRoot": "$t" - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - } - ] - }, - "command_name": "update", - "database_name": "crud-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "u": { - "v": 1 - }, - "foo": 1 - }, - { - "_id": 2, - "x": 2, - "y": 1 - } - ] - } - } - }, - { - "description": "UpdateMany in bulk write using pipelines", - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - } - } - ] - }, - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": {}, - "u": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ], - "multi": true - } - ] - }, - "command_name": "update", - "database_name": "crud-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "x": 1, - "foo": 1 - }, - { - "_id": 2, - "x": 2, - "foo": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-reads/aggregate-serverErrors.json b/tests/SpecTests/retryable-reads/aggregate-serverErrors.json index 04208bc95..1155f808d 100644 --- a/tests/SpecTests/retryable-reads/aggregate-serverErrors.json +++ b/tests/SpecTests/retryable-reads/aggregate-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -218,7 +219,7 @@ ] }, { - "description": "Aggregate succeeds after NotMaster", + "description": "Aggregate succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -311,7 +312,7 @@ ] }, { - "description": "Aggregate succeeds after NotMasterNoSlaveOk", + "description": "Aggregate succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -404,7 +405,7 @@ ] }, { - "description": "Aggregate succeeds after NotMasterOrSecondary", + "description": "Aggregate succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -1055,7 +1056,7 @@ ] }, { - "description": "Aggregate fails after two NotMaster errors", + "description": "Aggregate fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -1139,7 +1140,7 @@ ] }, { - "description": "Aggregate fails after NotMaster when retryReads is false", + "description": "Aggregate fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/aggregate.json b/tests/SpecTests/retryable-reads/aggregate.json index 30a6e05e6..f23d5c679 100644 --- a/tests/SpecTests/retryable-reads/aggregate.json +++ b/tests/SpecTests/retryable-reads/aggregate.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/changeStreams-client.watch-serverErrors.json b/tests/SpecTests/retryable-reads/changeStreams-client.watch-serverErrors.json index cf6c230ec..73dbfee91 100644 --- a/tests/SpecTests/retryable-reads/changeStreams-client.watch-serverErrors.json +++ b/tests/SpecTests/retryable-reads/changeStreams-client.watch-serverErrors.json @@ -9,8 +9,10 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" - ] + "sharded", + "load-balanced" + ], + "serverless": "forbid" } ], "database_name": "retryable-reads-tests", @@ -141,7 +143,7 @@ ] }, { - "description": "client.watch succeeds after NotMaster", + "description": "client.watch succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -196,7 +198,7 @@ ] }, { - "description": "client.watch succeeds after NotMasterNoSlaveOk", + "description": "client.watch succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -251,7 +253,7 @@ ] }, { - "description": "client.watch succeeds after NotMasterOrSecondary", + "description": "client.watch succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -636,7 +638,7 @@ ] }, { - "description": "client.watch fails after two NotMaster errors", + "description": "client.watch fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -692,7 +694,7 @@ ] }, { - "description": "client.watch fails after NotMaster when retryReads is false", + "description": "client.watch fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/changeStreams-client.watch.json b/tests/SpecTests/retryable-reads/changeStreams-client.watch.json index 9a2ccad09..30a53037a 100644 --- a/tests/SpecTests/retryable-reads/changeStreams-client.watch.json +++ b/tests/SpecTests/retryable-reads/changeStreams-client.watch.json @@ -9,8 +9,10 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" - ] + "sharded", + "load-balanced" + ], + "serverless": "forbid" } ], "database_name": "retryable-reads-tests", diff --git a/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch-serverErrors.json b/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch-serverErrors.json index eb7df1e26..77b3af04f 100644 --- a/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch-serverErrors.json +++ b/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch-serverErrors.json @@ -9,8 +9,10 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" - ] + "sharded", + "load-balanced" + ], + "serverless": "forbid" } ], "database_name": "retryable-reads-tests", @@ -133,7 +135,7 @@ ] }, { - "description": "db.coll.watch succeeds after NotMaster", + "description": "db.coll.watch succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -184,7 +186,7 @@ ] }, { - "description": "db.coll.watch succeeds after NotMasterNoSlaveOk", + "description": "db.coll.watch succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -235,7 +237,7 @@ ] }, { - "description": "db.coll.watch succeeds after NotMasterOrSecondary", + "description": "db.coll.watch succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -592,7 +594,7 @@ ] }, { - "description": "db.coll.watch fails after two NotMaster errors", + "description": "db.coll.watch fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -644,7 +646,7 @@ ] }, { - "description": "db.coll.watch fails after NotMaster when retryReads is false", + "description": "db.coll.watch fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch.json b/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch.json index 3408c8423..27f6105a4 100644 --- a/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch.json +++ b/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch.json @@ -9,8 +9,10 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" - ] + "sharded", + "load-balanced" + ], + "serverless": "forbid" } ], "database_name": "retryable-reads-tests", diff --git a/tests/SpecTests/retryable-reads/changeStreams-db.watch-serverErrors.json b/tests/SpecTests/retryable-reads/changeStreams-db.watch-serverErrors.json index e070f56a0..7a8753450 100644 --- a/tests/SpecTests/retryable-reads/changeStreams-db.watch-serverErrors.json +++ b/tests/SpecTests/retryable-reads/changeStreams-db.watch-serverErrors.json @@ -9,8 +9,10 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" - ] + "sharded", + "load-balanced" + ], + "serverless": "forbid" } ], "database_name": "retryable-reads-tests", @@ -133,7 +135,7 @@ ] }, { - "description": "db.watch succeeds after NotMaster", + "description": "db.watch succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -184,7 +186,7 @@ ] }, { - "description": "db.watch succeeds after NotMasterNoSlaveOk", + "description": "db.watch succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -235,7 +237,7 @@ ] }, { - "description": "db.watch succeeds after NotMasterOrSecondary", + "description": "db.watch succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -592,7 +594,7 @@ ] }, { - "description": "db.watch fails after two NotMaster errors", + "description": "db.watch fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -644,7 +646,7 @@ ] }, { - "description": "db.watch fails after NotMaster when retryReads is false", + "description": "db.watch fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/changeStreams-db.watch.json b/tests/SpecTests/retryable-reads/changeStreams-db.watch.json index bec09c49b..e6b0b9b78 100644 --- a/tests/SpecTests/retryable-reads/changeStreams-db.watch.json +++ b/tests/SpecTests/retryable-reads/changeStreams-db.watch.json @@ -9,8 +9,10 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" - ] + "sharded", + "load-balanced" + ], + "serverless": "forbid" } ], "database_name": "retryable-reads-tests", diff --git a/tests/SpecTests/retryable-reads/count-serverErrors.json b/tests/SpecTests/retryable-reads/count-serverErrors.json index 839680fe5..36a0c17ca 100644 --- a/tests/SpecTests/retryable-reads/count-serverErrors.json +++ b/tests/SpecTests/retryable-reads/count-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -114,7 +115,7 @@ ] }, { - "description": "Count succeeds after NotMaster", + "description": "Count succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -157,7 +158,7 @@ ] }, { - "description": "Count succeeds after NotMasterNoSlaveOk", + "description": "Count succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -200,7 +201,7 @@ ] }, { - "description": "Count succeeds after NotMasterOrSecondary", + "description": "Count succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -501,7 +502,7 @@ ] }, { - "description": "Count fails after two NotMaster errors", + "description": "Count fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -544,7 +545,7 @@ ] }, { - "description": "Count fails after NotMaster when retryReads is false", + "description": "Count fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/count.json b/tests/SpecTests/retryable-reads/count.json index 0ccf4982b..139a54513 100644 --- a/tests/SpecTests/retryable-reads/count.json +++ b/tests/SpecTests/retryable-reads/count.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/countDocuments-serverErrors.json b/tests/SpecTests/retryable-reads/countDocuments-serverErrors.json index f45eadfa0..782ea5e4f 100644 --- a/tests/SpecTests/retryable-reads/countDocuments-serverErrors.json +++ b/tests/SpecTests/retryable-reads/countDocuments-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -166,7 +167,7 @@ ] }, { - "description": "CountDocuments succeeds after NotMaster", + "description": "CountDocuments succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -235,7 +236,7 @@ ] }, { - "description": "CountDocuments succeeds after NotMasterNoSlaveOk", + "description": "CountDocuments succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -304,7 +305,7 @@ ] }, { - "description": "CountDocuments succeeds after NotMasterOrSecondary", + "description": "CountDocuments succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -787,7 +788,7 @@ ] }, { - "description": "CountDocuments fails after two NotMaster errors", + "description": "CountDocuments fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -856,7 +857,7 @@ ] }, { - "description": "CountDocuments fails after NotMaster when retryReads is false", + "description": "CountDocuments fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/countDocuments.json b/tests/SpecTests/retryable-reads/countDocuments.json index b4ccf3668..57a64e45b 100644 --- a/tests/SpecTests/retryable-reads/countDocuments.json +++ b/tests/SpecTests/retryable-reads/countDocuments.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/distinct-serverErrors.json b/tests/SpecTests/retryable-reads/distinct-serverErrors.json index 50fd6a550..d7c6018a6 100644 --- a/tests/SpecTests/retryable-reads/distinct-serverErrors.json +++ b/tests/SpecTests/retryable-reads/distinct-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -158,7 +159,7 @@ ] }, { - "description": "Distinct succeeds after NotMaster", + "description": "Distinct succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -221,7 +222,7 @@ ] }, { - "description": "Distinct succeeds after NotMasterNoSlaveOk", + "description": "Distinct succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -284,7 +285,7 @@ ] }, { - "description": "Distinct succeeds after NotMasterOrSecondary", + "description": "Distinct succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -725,7 +726,7 @@ ] }, { - "description": "Distinct fails after two NotMaster errors", + "description": "Distinct fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -785,7 +786,7 @@ ] }, { - "description": "Distinct fails after NotMaster when retryReads is false", + "description": "Distinct fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/distinct.json b/tests/SpecTests/retryable-reads/distinct.json index b5885e27e..1fd415da8 100644 --- a/tests/SpecTests/retryable-reads/distinct.json +++ b/tests/SpecTests/retryable-reads/distinct.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/estimatedDocumentCount-serverErrors.json b/tests/SpecTests/retryable-reads/estimatedDocumentCount-serverErrors.json index 1af21d1fe..6bb128f5f 100644 --- a/tests/SpecTests/retryable-reads/estimatedDocumentCount-serverErrors.json +++ b/tests/SpecTests/retryable-reads/estimatedDocumentCount-serverErrors.json @@ -108,7 +108,7 @@ ] }, { - "description": "EstimatedDocumentCount succeeds after NotMaster", + "description": "EstimatedDocumentCount succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -148,7 +148,7 @@ ] }, { - "description": "EstimatedDocumentCount succeeds after NotMasterNoSlaveOk", + "description": "EstimatedDocumentCount succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -188,7 +188,7 @@ ] }, { - "description": "EstimatedDocumentCount succeeds after NotMasterOrSecondary", + "description": "EstimatedDocumentCount succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -468,7 +468,7 @@ ] }, { - "description": "EstimatedDocumentCount fails after two NotMaster errors", + "description": "EstimatedDocumentCount fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -508,7 +508,7 @@ ] }, { - "description": "EstimatedDocumentCount fails after NotMaster when retryReads is false", + "description": "EstimatedDocumentCount fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/find-serverErrors.json b/tests/SpecTests/retryable-reads/find-serverErrors.json index 44ecf34d2..f6b96c6dc 100644 --- a/tests/SpecTests/retryable-reads/find-serverErrors.json +++ b/tests/SpecTests/retryable-reads/find-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -188,7 +189,7 @@ ] }, { - "description": "Find succeeds after NotMaster", + "description": "Find succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -262,7 +263,7 @@ ] }, { - "description": "Find succeeds after NotMasterNoSlaveOk", + "description": "Find succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -336,7 +337,7 @@ ] }, { - "description": "Find succeeds after NotMasterOrSecondary", + "description": "Find succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -854,7 +855,7 @@ ] }, { - "description": "Find fails after two NotMaster errors", + "description": "Find fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -911,7 +912,7 @@ ] }, { - "description": "Find fails after NotMaster when retryReads is false", + "description": "Find fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/find.json b/tests/SpecTests/retryable-reads/find.json index 56479ff1d..00d419c0d 100644 --- a/tests/SpecTests/retryable-reads/find.json +++ b/tests/SpecTests/retryable-reads/find.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/findOne-serverErrors.json b/tests/SpecTests/retryable-reads/findOne-serverErrors.json index b8229483d..d039ef247 100644 --- a/tests/SpecTests/retryable-reads/findOne-serverErrors.json +++ b/tests/SpecTests/retryable-reads/findOne-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -148,7 +149,7 @@ ] }, { - "description": "FindOne succeeds after NotMaster", + "description": "FindOne succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -202,7 +203,7 @@ ] }, { - "description": "FindOne succeeds after NotMasterNoSlaveOk", + "description": "FindOne succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -256,7 +257,7 @@ ] }, { - "description": "FindOne succeeds after NotMasterOrSecondary", + "description": "FindOne succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -634,7 +635,7 @@ ] }, { - "description": "FindOne fails after two NotMaster errors", + "description": "FindOne fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -685,7 +686,7 @@ ] }, { - "description": "FindOne fails after NotMaster when retryReads is false", + "description": "FindOne fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/findOne.json b/tests/SpecTests/retryable-reads/findOne.json index d296a9cdb..b9deb73d2 100644 --- a/tests/SpecTests/retryable-reads/findOne.json +++ b/tests/SpecTests/retryable-reads/findOne.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/gridfs-download-serverErrors.json b/tests/SpecTests/retryable-reads/gridfs-download-serverErrors.json index 84e50e370..cec3a5016 100644 --- a/tests/SpecTests/retryable-reads/gridfs-download-serverErrors.json +++ b/tests/SpecTests/retryable-reads/gridfs-download-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -191,7 +192,7 @@ ] }, { - "description": "Download succeeds after NotMaster", + "description": "Download succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -261,7 +262,7 @@ ] }, { - "description": "Download succeeds after NotMasterNoSlaveOk", + "description": "Download succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -331,7 +332,7 @@ ] }, { - "description": "Download succeeds after NotMasterOrSecondary", + "description": "Download succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -821,7 +822,7 @@ ] }, { - "description": "Download fails after two NotMaster errors", + "description": "Download fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -876,7 +877,7 @@ ] }, { - "description": "Download fails after NotMaster when retryReads is false", + "description": "Download fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/gridfs-download.json b/tests/SpecTests/retryable-reads/gridfs-download.json index a5c5ef4d5..4d0d5a17e 100644 --- a/tests/SpecTests/retryable-reads/gridfs-download.json +++ b/tests/SpecTests/retryable-reads/gridfs-download.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/gridfs-downloadByName-serverErrors.json b/tests/SpecTests/retryable-reads/gridfs-downloadByName-serverErrors.json index de439ce4b..a64230d38 100644 --- a/tests/SpecTests/retryable-reads/gridfs-downloadByName-serverErrors.json +++ b/tests/SpecTests/retryable-reads/gridfs-downloadByName-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -179,7 +180,7 @@ ] }, { - "description": "DownloadByName succeeds after NotMaster", + "description": "DownloadByName succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -243,7 +244,7 @@ ] }, { - "description": "DownloadByName succeeds after NotMasterNoSlaveOk", + "description": "DownloadByName succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -307,7 +308,7 @@ ] }, { - "description": "DownloadByName succeeds after NotMasterOrSecondary", + "description": "DownloadByName succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -755,7 +756,7 @@ ] }, { - "description": "DownloadByName fails after two NotMaster errors", + "description": "DownloadByName fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -804,7 +805,7 @@ ] }, { - "description": "DownloadByName fails after NotMaster when retryReads is false", + "description": "DownloadByName fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/gridfs-downloadByName.json b/tests/SpecTests/retryable-reads/gridfs-downloadByName.json index 0634a09bf..48f2168cf 100644 --- a/tests/SpecTests/retryable-reads/gridfs-downloadByName.json +++ b/tests/SpecTests/retryable-reads/gridfs-downloadByName.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/listCollectionNames-serverErrors.json b/tests/SpecTests/retryable-reads/listCollectionNames-serverErrors.json index 27c13d630..bbdce625a 100644 --- a/tests/SpecTests/retryable-reads/listCollectionNames-serverErrors.json +++ b/tests/SpecTests/retryable-reads/listCollectionNames-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -93,7 +94,7 @@ ] }, { - "description": "ListCollectionNames succeeds after NotMaster", + "description": "ListCollectionNames succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -130,7 +131,7 @@ ] }, { - "description": "ListCollectionNames succeeds after NotMasterNoSlaveOk", + "description": "ListCollectionNames succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -167,7 +168,7 @@ ] }, { - "description": "ListCollectionNames succeeds after NotMasterOrSecondary", + "description": "ListCollectionNames succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -426,7 +427,7 @@ ] }, { - "description": "ListCollectionNames fails after two NotMaster errors", + "description": "ListCollectionNames fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -464,7 +465,7 @@ ] }, { - "description": "ListCollectionNames fails after NotMaster when retryReads is false", + "description": "ListCollectionNames fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/listCollectionNames.json b/tests/SpecTests/retryable-reads/listCollectionNames.json index 437fc36a4..73d96a3cf 100644 --- a/tests/SpecTests/retryable-reads/listCollectionNames.json +++ b/tests/SpecTests/retryable-reads/listCollectionNames.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/listCollectionObjects-serverErrors.json b/tests/SpecTests/retryable-reads/listCollectionObjects-serverErrors.json index 3922713df..ab469dfe3 100644 --- a/tests/SpecTests/retryable-reads/listCollectionObjects-serverErrors.json +++ b/tests/SpecTests/retryable-reads/listCollectionObjects-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -93,7 +94,7 @@ ] }, { - "description": "ListCollectionObjects succeeds after NotMaster", + "description": "ListCollectionObjects succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -130,7 +131,7 @@ ] }, { - "description": "ListCollectionObjects succeeds after NotMasterNoSlaveOk", + "description": "ListCollectionObjects succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -167,7 +168,7 @@ ] }, { - "description": "ListCollectionObjects succeeds after NotMasterOrSecondary", + "description": "ListCollectionObjects succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -426,7 +427,7 @@ ] }, { - "description": "ListCollectionObjects fails after two NotMaster errors", + "description": "ListCollectionObjects fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -464,7 +465,7 @@ ] }, { - "description": "ListCollectionObjects fails after NotMaster when retryReads is false", + "description": "ListCollectionObjects fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/listCollectionObjects.json b/tests/SpecTests/retryable-reads/listCollectionObjects.json index 1f537b743..1fb0f1843 100644 --- a/tests/SpecTests/retryable-reads/listCollectionObjects.json +++ b/tests/SpecTests/retryable-reads/listCollectionObjects.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/listCollections-serverErrors.json b/tests/SpecTests/retryable-reads/listCollections-serverErrors.json index 6972073b1..def9ac459 100644 --- a/tests/SpecTests/retryable-reads/listCollections-serverErrors.json +++ b/tests/SpecTests/retryable-reads/listCollections-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -93,7 +94,7 @@ ] }, { - "description": "ListCollections succeeds after NotMaster", + "description": "ListCollections succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -130,7 +131,7 @@ ] }, { - "description": "ListCollections succeeds after NotMasterNoSlaveOk", + "description": "ListCollections succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -167,7 +168,7 @@ ] }, { - "description": "ListCollections succeeds after NotMasterOrSecondary", + "description": "ListCollections succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -426,7 +427,7 @@ ] }, { - "description": "ListCollections fails after two NotMaster errors", + "description": "ListCollections fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -464,7 +465,7 @@ ] }, { - "description": "ListCollections fails after NotMaster when retryReads is false", + "description": "ListCollections fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/listCollections.json b/tests/SpecTests/retryable-reads/listCollections.json index a6b452e64..242788362 100644 --- a/tests/SpecTests/retryable-reads/listCollections.json +++ b/tests/SpecTests/retryable-reads/listCollections.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/listDatabaseNames-serverErrors.json b/tests/SpecTests/retryable-reads/listDatabaseNames-serverErrors.json index 11faf58bf..1dd8e4415 100644 --- a/tests/SpecTests/retryable-reads/listDatabaseNames-serverErrors.json +++ b/tests/SpecTests/retryable-reads/listDatabaseNames-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -93,7 +94,7 @@ ] }, { - "description": "ListDatabaseNames succeeds after NotMaster", + "description": "ListDatabaseNames succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -130,7 +131,7 @@ ] }, { - "description": "ListDatabaseNames succeeds after NotMasterNoSlaveOk", + "description": "ListDatabaseNames succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -167,7 +168,7 @@ ] }, { - "description": "ListDatabaseNames succeeds after NotMasterOrSecondary", + "description": "ListDatabaseNames succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -426,7 +427,7 @@ ] }, { - "description": "ListDatabaseNames fails after two NotMaster errors", + "description": "ListDatabaseNames fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -464,7 +465,7 @@ ] }, { - "description": "ListDatabaseNames fails after NotMaster when retryReads is false", + "description": "ListDatabaseNames fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/listDatabaseNames.json b/tests/SpecTests/retryable-reads/listDatabaseNames.json index b35f7ab18..b431f5701 100644 --- a/tests/SpecTests/retryable-reads/listDatabaseNames.json +++ b/tests/SpecTests/retryable-reads/listDatabaseNames.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/listDatabaseObjects-serverErrors.json b/tests/SpecTests/retryable-reads/listDatabaseObjects-serverErrors.json index 38082f2e2..bc497bb08 100644 --- a/tests/SpecTests/retryable-reads/listDatabaseObjects-serverErrors.json +++ b/tests/SpecTests/retryable-reads/listDatabaseObjects-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -93,7 +94,7 @@ ] }, { - "description": "ListDatabaseObjects succeeds after NotMaster", + "description": "ListDatabaseObjects succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -130,7 +131,7 @@ ] }, { - "description": "ListDatabaseObjects succeeds after NotMasterNoSlaveOk", + "description": "ListDatabaseObjects succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -167,7 +168,7 @@ ] }, { - "description": "ListDatabaseObjects succeeds after NotMasterOrSecondary", + "description": "ListDatabaseObjects succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -426,7 +427,7 @@ ] }, { - "description": "ListDatabaseObjects fails after two NotMaster errors", + "description": "ListDatabaseObjects fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -464,7 +465,7 @@ ] }, { - "description": "ListDatabaseObjects fails after NotMaster when retryReads is false", + "description": "ListDatabaseObjects fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/listDatabaseObjects.json b/tests/SpecTests/retryable-reads/listDatabaseObjects.json index cbd2c6763..267fe921c 100644 --- a/tests/SpecTests/retryable-reads/listDatabaseObjects.json +++ b/tests/SpecTests/retryable-reads/listDatabaseObjects.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/listDatabases-serverErrors.json b/tests/SpecTests/retryable-reads/listDatabases-serverErrors.json index 4047f749f..ed7bcbc39 100644 --- a/tests/SpecTests/retryable-reads/listDatabases-serverErrors.json +++ b/tests/SpecTests/retryable-reads/listDatabases-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -93,7 +94,7 @@ ] }, { - "description": "ListDatabases succeeds after NotMaster", + "description": "ListDatabases succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -130,7 +131,7 @@ ] }, { - "description": "ListDatabases succeeds after NotMasterNoSlaveOk", + "description": "ListDatabases succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -167,7 +168,7 @@ ] }, { - "description": "ListDatabases succeeds after NotMasterOrSecondary", + "description": "ListDatabases succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -426,7 +427,7 @@ ] }, { - "description": "ListDatabases fails after two NotMaster errors", + "description": "ListDatabases fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -464,7 +465,7 @@ ] }, { - "description": "ListDatabases fails after NotMaster when retryReads is false", + "description": "ListDatabases fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/listDatabases.json b/tests/SpecTests/retryable-reads/listDatabases.json index 3cb8bbd08..69ef9788f 100644 --- a/tests/SpecTests/retryable-reads/listDatabases.json +++ b/tests/SpecTests/retryable-reads/listDatabases.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/listIndexNames-serverErrors.json b/tests/SpecTests/retryable-reads/listIndexNames-serverErrors.json index 1a9ba83bc..2d3265ec8 100644 --- a/tests/SpecTests/retryable-reads/listIndexNames-serverErrors.json +++ b/tests/SpecTests/retryable-reads/listIndexNames-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -97,7 +98,7 @@ ] }, { - "description": "ListIndexNames succeeds after NotMaster", + "description": "ListIndexNames succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -136,7 +137,7 @@ ] }, { - "description": "ListIndexNames succeeds after NotMasterNoSlaveOk", + "description": "ListIndexNames succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -175,7 +176,7 @@ ] }, { - "description": "ListIndexNames succeeds after NotMasterOrSecondary", + "description": "ListIndexNames succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -448,7 +449,7 @@ ] }, { - "description": "ListIndexNames fails after two NotMaster errors", + "description": "ListIndexNames fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -488,7 +489,7 @@ ] }, { - "description": "ListIndexNames fails after NotMaster when retryReads is false", + "description": "ListIndexNames fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/listIndexNames.json b/tests/SpecTests/retryable-reads/listIndexNames.json index 912c70601..fbdb420f8 100644 --- a/tests/SpecTests/retryable-reads/listIndexNames.json +++ b/tests/SpecTests/retryable-reads/listIndexNames.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/listIndexes-serverErrors.json b/tests/SpecTests/retryable-reads/listIndexes-serverErrors.json index 16b61d535..25c5b0e44 100644 --- a/tests/SpecTests/retryable-reads/listIndexes-serverErrors.json +++ b/tests/SpecTests/retryable-reads/listIndexes-serverErrors.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -97,7 +98,7 @@ ] }, { - "description": "ListIndexes succeeds after NotMaster", + "description": "ListIndexes succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -136,7 +137,7 @@ ] }, { - "description": "ListIndexes succeeds after NotMasterNoSlaveOk", + "description": "ListIndexes succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -175,7 +176,7 @@ ] }, { - "description": "ListIndexes succeeds after NotMasterOrSecondary", + "description": "ListIndexes succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -448,7 +449,7 @@ ] }, { - "description": "ListIndexes fails after two NotMaster errors", + "description": "ListIndexes fails after two NotWritablePrimary errors", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -488,7 +489,7 @@ ] }, { - "description": "ListIndexes fails after NotMaster when retryReads is false", + "description": "ListIndexes fails after NotWritablePrimary when retryReads is false", "clientOptions": { "retryReads": false }, diff --git a/tests/SpecTests/retryable-reads/listIndexes.json b/tests/SpecTests/retryable-reads/listIndexes.json index f460ea768..5cb620ae4 100644 --- a/tests/SpecTests/retryable-reads/listIndexes.json +++ b/tests/SpecTests/retryable-reads/listIndexes.json @@ -10,7 +10,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-reads/mapReduce.json b/tests/SpecTests/retryable-reads/mapReduce.json index 3cb35fd49..9327a2305 100644 --- a/tests/SpecTests/retryable-reads/mapReduce.json +++ b/tests/SpecTests/retryable-reads/mapReduce.json @@ -10,8 +10,10 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" - ] + "sharded", + "load-balanced" + ], + "serverless": "forbid" } ], "database_name": "retryable-reads-tests", @@ -50,8 +52,8 @@ }, "result": [ { - "_id": 0.0, - "value": 6.0 + "_id": 0, + "value": 6 } ] } diff --git a/tests/SpecTests/retryable-writes/bulkWrite-errorLabels.json b/tests/SpecTests/retryable-writes/bulkWrite-errorLabels.json index 94ea3ea98..66c3ecb33 100644 --- a/tests/SpecTests/retryable-writes/bulkWrite-errorLabels.json +++ b/tests/SpecTests/retryable-writes/bulkWrite-errorLabels.json @@ -4,7 +4,8 @@ "minServerVersion": "4.3.1", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-writes/bulkWrite-serverErrors.json b/tests/SpecTests/retryable-writes/bulkWrite-serverErrors.json index d9561d568..1e6cc74c0 100644 --- a/tests/SpecTests/retryable-writes/bulkWrite-serverErrors.json +++ b/tests/SpecTests/retryable-writes/bulkWrite-serverErrors.json @@ -9,7 +9,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -118,12 +119,12 @@ "failCommands": [ "insert" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, diff --git a/tests/SpecTests/retryable-writes/deleteMany.json b/tests/SpecTests/retryable-writes/deleteMany.json index 642ad11fb..faa21c44f 100644 --- a/tests/SpecTests/retryable-writes/deleteMany.json +++ b/tests/SpecTests/retryable-writes/deleteMany.json @@ -4,7 +4,8 @@ "minServerVersion": "3.6", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-writes/deleteOne-errorLabels.json b/tests/SpecTests/retryable-writes/deleteOne-errorLabels.json index bff02e1f9..c14692fd1 100644 --- a/tests/SpecTests/retryable-writes/deleteOne-errorLabels.json +++ b/tests/SpecTests/retryable-writes/deleteOne-errorLabels.json @@ -4,7 +4,8 @@ "minServerVersion": "4.3.1", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-writes/deleteOne-serverErrors.json b/tests/SpecTests/retryable-writes/deleteOne-serverErrors.json index 69d225759..a1a27838d 100644 --- a/tests/SpecTests/retryable-writes/deleteOne-serverErrors.json +++ b/tests/SpecTests/retryable-writes/deleteOne-serverErrors.json @@ -9,7 +9,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -74,12 +75,12 @@ "failCommands": [ "delete" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, diff --git a/tests/SpecTests/retryable-writes/findOneAndDelete-errorLabels.json b/tests/SpecTests/retryable-writes/findOneAndDelete-errorLabels.json index efa62dba2..60e6e0a7b 100644 --- a/tests/SpecTests/retryable-writes/findOneAndDelete-errorLabels.json +++ b/tests/SpecTests/retryable-writes/findOneAndDelete-errorLabels.json @@ -4,7 +4,8 @@ "minServerVersion": "4.3.1", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-writes/findOneAndDelete-serverErrors.json b/tests/SpecTests/retryable-writes/findOneAndDelete-serverErrors.json index 0785e5d03..c18b63f45 100644 --- a/tests/SpecTests/retryable-writes/findOneAndDelete-serverErrors.json +++ b/tests/SpecTests/retryable-writes/findOneAndDelete-serverErrors.json @@ -9,7 +9,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -80,12 +81,12 @@ "failCommands": [ "findAndModify" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, diff --git a/tests/SpecTests/retryable-writes/findOneAndReplace-errorLabels.json b/tests/SpecTests/retryable-writes/findOneAndReplace-errorLabels.json index d9473d139..afa2f47af 100644 --- a/tests/SpecTests/retryable-writes/findOneAndReplace-errorLabels.json +++ b/tests/SpecTests/retryable-writes/findOneAndReplace-errorLabels.json @@ -4,7 +4,8 @@ "minServerVersion": "4.3.1", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-writes/findOneAndReplace-serverErrors.json b/tests/SpecTests/retryable-writes/findOneAndReplace-serverErrors.json index 6ebe057cf..944a3af84 100644 --- a/tests/SpecTests/retryable-writes/findOneAndReplace-serverErrors.json +++ b/tests/SpecTests/retryable-writes/findOneAndReplace-serverErrors.json @@ -9,7 +9,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -84,12 +85,12 @@ "failCommands": [ "findAndModify" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, diff --git a/tests/SpecTests/retryable-writes/findOneAndUpdate-errorLabels.json b/tests/SpecTests/retryable-writes/findOneAndUpdate-errorLabels.json index 1926d7fa5..19b3a9e77 100644 --- a/tests/SpecTests/retryable-writes/findOneAndUpdate-errorLabels.json +++ b/tests/SpecTests/retryable-writes/findOneAndUpdate-errorLabels.json @@ -4,7 +4,8 @@ "minServerVersion": "4.3.1", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-writes/findOneAndUpdate-serverErrors.json b/tests/SpecTests/retryable-writes/findOneAndUpdate-serverErrors.json index e6e369c13..e83a61061 100644 --- a/tests/SpecTests/retryable-writes/findOneAndUpdate-serverErrors.json +++ b/tests/SpecTests/retryable-writes/findOneAndUpdate-serverErrors.json @@ -9,7 +9,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -85,12 +86,12 @@ "failCommands": [ "findAndModify" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, diff --git a/tests/SpecTests/retryable-writes/insertMany-errorLabels.json b/tests/SpecTests/retryable-writes/insertMany-errorLabels.json index c78946e90..65fd377fa 100644 --- a/tests/SpecTests/retryable-writes/insertMany-errorLabels.json +++ b/tests/SpecTests/retryable-writes/insertMany-errorLabels.json @@ -4,7 +4,8 @@ "minServerVersion": "4.3.1", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-writes/insertMany-serverErrors.json b/tests/SpecTests/retryable-writes/insertMany-serverErrors.json index 1c6ebafc2..fe8dbf4a6 100644 --- a/tests/SpecTests/retryable-writes/insertMany-serverErrors.json +++ b/tests/SpecTests/retryable-writes/insertMany-serverErrors.json @@ -9,7 +9,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -91,12 +92,12 @@ "failCommands": [ "insert" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, diff --git a/tests/SpecTests/retryable-writes/insertOne-errorLabels.json b/tests/SpecTests/retryable-writes/insertOne-errorLabels.json index 9b8d13d52..d90ac5dfb 100644 --- a/tests/SpecTests/retryable-writes/insertOne-errorLabels.json +++ b/tests/SpecTests/retryable-writes/insertOne-errorLabels.json @@ -4,7 +4,8 @@ "minServerVersion": "4.3.1", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-writes/insertOne-serverErrors.json b/tests/SpecTests/retryable-writes/insertOne-serverErrors.json index cb1e6f826..5179a6ab7 100644 --- a/tests/SpecTests/retryable-writes/insertOne-serverErrors.json +++ b/tests/SpecTests/retryable-writes/insertOne-serverErrors.json @@ -9,7 +9,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -117,7 +118,7 @@ } }, { - "description": "InsertOne succeeds after NotMaster", + "description": "InsertOne succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -166,7 +167,7 @@ } }, { - "description": "InsertOne succeeds after NotMasterOrSecondary", + "description": "InsertOne succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -215,7 +216,7 @@ } }, { - "description": "InsertOne succeeds after NotMasterNoSlaveOk", + "description": "InsertOne succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -760,12 +761,12 @@ "failCommands": [ "insert" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 11600, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, @@ -811,12 +812,12 @@ "failCommands": [ "insert" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 11602, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, @@ -862,12 +863,12 @@ "failCommands": [ "insert" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 189, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, @@ -913,12 +914,12 @@ "failCommands": [ "insert" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, @@ -964,12 +965,12 @@ "failCommands": [ "insert" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, diff --git a/tests/SpecTests/retryable-writes/replaceOne-errorLabels.json b/tests/SpecTests/retryable-writes/replaceOne-errorLabels.json index 06867e515..6029b875d 100644 --- a/tests/SpecTests/retryable-writes/replaceOne-errorLabels.json +++ b/tests/SpecTests/retryable-writes/replaceOne-errorLabels.json @@ -4,7 +4,8 @@ "minServerVersion": "4.3.1", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-writes/replaceOne-serverErrors.json b/tests/SpecTests/retryable-writes/replaceOne-serverErrors.json index af18bcf1a..6b35722e1 100644 --- a/tests/SpecTests/retryable-writes/replaceOne-serverErrors.json +++ b/tests/SpecTests/retryable-writes/replaceOne-serverErrors.json @@ -9,7 +9,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -84,12 +85,12 @@ "failCommands": [ "update" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, diff --git a/tests/SpecTests/retryable-writes/updateMany.json b/tests/SpecTests/retryable-writes/updateMany.json index 14288c286..46fef73e7 100644 --- a/tests/SpecTests/retryable-writes/updateMany.json +++ b/tests/SpecTests/retryable-writes/updateMany.json @@ -4,7 +4,8 @@ "minServerVersion": "3.6", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-writes/updateOne-errorLabels.json b/tests/SpecTests/retryable-writes/updateOne-errorLabels.json index 4a6be3ffb..5bd00cde9 100644 --- a/tests/SpecTests/retryable-writes/updateOne-errorLabels.json +++ b/tests/SpecTests/retryable-writes/updateOne-errorLabels.json @@ -4,7 +4,8 @@ "minServerVersion": "4.3.1", "topology": [ "replicaset", - "sharded" + "sharded", + "load-balanced" ] } ], diff --git a/tests/SpecTests/retryable-writes/updateOne-serverErrors.json b/tests/SpecTests/retryable-writes/updateOne-serverErrors.json index bb442eb68..cf274f57e 100644 --- a/tests/SpecTests/retryable-writes/updateOne-serverErrors.json +++ b/tests/SpecTests/retryable-writes/updateOne-serverErrors.json @@ -9,7 +9,8 @@ { "minServerVersion": "4.1.7", "topology": [ - "sharded" + "sharded", + "load-balanced" ] } ], @@ -85,12 +86,12 @@ "failCommands": [ "update" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, diff --git a/tests/SpecTests/transactions-convenient-api/commit-retry.json b/tests/SpecTests/transactions-convenient-api/commit-retry.json index 312116253..02e38460d 100644 --- a/tests/SpecTests/transactions-convenient-api/commit-retry.json +++ b/tests/SpecTests/transactions-convenient-api/commit-retry.json @@ -293,7 +293,7 @@ } }, { - "description": "commit is retried after commitTransaction UnknownTransactionCommitResult (NotMaster)", + "description": "commit is retried after commitTransaction UnknownTransactionCommitResult (NotWritablePrimary)", "failPoint": { "configureFailPoint": "failCommand", "mode": { diff --git a/tests/SpecTests/transactions/error-labels.json b/tests/SpecTests/transactions/error-labels.json index 2d3eed3cc..0be19c731 100644 --- a/tests/SpecTests/transactions/error-labels.json +++ b/tests/SpecTests/transactions/error-labels.json @@ -10,7 +10,8 @@ "minServerVersion": "4.1.8", "topology": [ "sharded" - ] + ], + "serverless": "forbid" } ], "database_name": "transaction-tests", @@ -102,7 +103,7 @@ } }, { - "description": "NotMaster errors contain transient label", + "description": "NotWritablePrimary errors contain transient label", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -962,12 +963,12 @@ "failCommands": [ "commitTransaction" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } }, diff --git a/tests/SpecTests/transactions/errors-client.json b/tests/SpecTests/transactions/errors-client.json index 4bd1c0eb3..15fae96fe 100644 --- a/tests/SpecTests/transactions/errors-client.json +++ b/tests/SpecTests/transactions/errors-client.json @@ -25,14 +25,15 @@ "object": "session0" }, { - "name": "insertOne", + "name": "updateOne", "object": "collection", "arguments": { "session": "session0", - "document": { - "_id": { - ".": "." - } + "filter": { + "_id": 1 + }, + "update": { + "x": 1 } }, "error": true @@ -60,22 +61,23 @@ "arguments": { "session": "session0", "document": { - "_id": 4 + "_id": 1 } }, "result": { - "insertedId": 4 + "insertedId": 1 } }, { - "name": "insertOne", + "name": "updateOne", "object": "collection", "arguments": { "session": "session0", - "document": { - "_id": { - ".": "." - } + "filter": { + "_id": 1 + }, + "update": { + "x": 1 } }, "error": true diff --git a/tests/SpecTests/transactions/mongos-pin-auto.json b/tests/SpecTests/transactions/mongos-pin-auto.json index f6ede5268..037f212f4 100644 --- a/tests/SpecTests/transactions/mongos-pin-auto.json +++ b/tests/SpecTests/transactions/mongos-pin-auto.json @@ -4,7 +4,8 @@ "minServerVersion": "4.1.8", "topology": [ "sharded" - ] + ], + "serverless": "forbid" } ], "database_name": "transaction-tests", diff --git a/tests/SpecTests/transactions/mongos-recovery-token.json b/tests/SpecTests/transactions/mongos-recovery-token.json index 35ef45a03..da4e9861d 100644 --- a/tests/SpecTests/transactions/mongos-recovery-token.json +++ b/tests/SpecTests/transactions/mongos-recovery-token.json @@ -4,7 +4,8 @@ "minServerVersion": "4.1.8", "topology": [ "sharded" - ] + ], + "serverless": "forbid" } ], "database_name": "transaction-tests", @@ -179,12 +180,12 @@ "failCommands": [ "commitTransaction" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errmsg": "Replication is being shut down", - "errorLabels": [ - "RetryableWriteError" - ] + "errmsg": "Replication is being shut down" } } } @@ -306,7 +307,8 @@ "data": { "failCommands": [ "commitTransaction", - "isMaster" + "isMaster", + "hello" ], "closeConnection": true } diff --git a/tests/SpecTests/transactions/pin-mongos.json b/tests/SpecTests/transactions/pin-mongos.json index 8e9d049d0..485a3d932 100644 --- a/tests/SpecTests/transactions/pin-mongos.json +++ b/tests/SpecTests/transactions/pin-mongos.json @@ -4,7 +4,8 @@ "minServerVersion": "4.1.8", "topology": [ "sharded" - ] + ], + "serverless": "forbid" } ], "database_name": "transaction-tests", @@ -1106,7 +1107,8 @@ "data": { "failCommands": [ "insert", - "isMaster" + "isMaster", + "hello" ], "closeConnection": true } diff --git a/tests/SpecTests/transactions/retryable-abort.json b/tests/SpecTests/transactions/retryable-abort.json index 5a3aaa7bf..13cc7c88f 100644 --- a/tests/SpecTests/transactions/retryable-abort.json +++ b/tests/SpecTests/transactions/retryable-abort.json @@ -402,7 +402,7 @@ } }, { - "description": "abortTransaction succeeds after NotMaster", + "description": "abortTransaction succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -506,7 +506,7 @@ } }, { - "description": "abortTransaction succeeds after NotMasterOrSecondary", + "description": "abortTransaction succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -610,7 +610,7 @@ } }, { - "description": "abortTransaction succeeds after NotMasterNoSlaveOk", + "description": "abortTransaction succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -1556,11 +1556,11 @@ "failCommands": [ "abortTransaction" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 11600, - "errorLabels": [ - "RetryableWriteError" - ], "errmsg": "Replication is being shut down" } } @@ -1673,11 +1673,11 @@ "failCommands": [ "abortTransaction" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 11602, - "errorLabels": [ - "RetryableWriteError" - ], "errmsg": "Replication is being shut down" } } @@ -1790,11 +1790,11 @@ "failCommands": [ "abortTransaction" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 189, - "errorLabels": [ - "RetryableWriteError" - ], "errmsg": "Replication is being shut down" } } @@ -1907,11 +1907,11 @@ "failCommands": [ "abortTransaction" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errorLabels": [ - "RetryableWriteError" - ], "errmsg": "Replication is being shut down" } } diff --git a/tests/SpecTests/transactions/retryable-commit.json b/tests/SpecTests/transactions/retryable-commit.json index 4895c6e0c..49148c62d 100644 --- a/tests/SpecTests/transactions/retryable-commit.json +++ b/tests/SpecTests/transactions/retryable-commit.json @@ -624,7 +624,7 @@ } }, { - "description": "commitTransaction succeeds after NotMaster", + "description": "commitTransaction succeeds after NotWritablePrimary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -735,7 +735,7 @@ } }, { - "description": "commitTransaction succeeds after NotMasterOrSecondary", + "description": "commitTransaction succeeds after NotPrimaryOrSecondary", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -846,7 +846,7 @@ } }, { - "description": "commitTransaction succeeds after NotMasterNoSlaveOk", + "description": "commitTransaction succeeds after NotPrimaryNoSecondaryOk", "failPoint": { "configureFailPoint": "failCommand", "mode": { @@ -1855,11 +1855,11 @@ "failCommands": [ "commitTransaction" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 11600, - "errorLabels": [ - "RetryableWriteError" - ], "errmsg": "Replication is being shut down" } } @@ -1977,11 +1977,11 @@ "failCommands": [ "commitTransaction" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 11602, - "errorLabels": [ - "RetryableWriteError" - ], "errmsg": "Replication is being shut down" } } @@ -2099,11 +2099,11 @@ "failCommands": [ "commitTransaction" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 189, - "errorLabels": [ - "RetryableWriteError" - ], "errmsg": "Replication is being shut down" } } @@ -2221,11 +2221,11 @@ "failCommands": [ "commitTransaction" ], + "errorLabels": [ + "RetryableWriteError" + ], "writeConcernError": { "code": 91, - "errorLabels": [ - "RetryableWriteError" - ], "errmsg": "Replication is being shut down" } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 16e50da1e..91e1244de 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -12,6 +12,7 @@ use ReflectionClass; use stdClass; use Traversable; + use function array_map; use function array_merge; use function array_values; @@ -27,16 +28,15 @@ use function restore_error_handler; use function set_error_handler; use function sprintf; + use const E_USER_DEPRECATED; abstract class TestCase extends BaseTestCase { /** * Return the connection URI. - * - * @return string */ - public static function getUri() + public static function getUri(): string { return getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1:27017'; } @@ -50,7 +50,7 @@ public static function getUri() * @param array|object $expectedDocument * @param array|object $actualDocument */ - public function assertMatchesDocument($expectedDocument, $actualDocument) + public function assertMatchesDocument($expectedDocument, $actualDocument): void { $normalizedExpectedDocument = $this->normalizeBSON($expectedDocument); $normalizedActualDocument = $this->normalizeBSON($actualDocument); @@ -84,7 +84,7 @@ public function assertMatchesDocument($expectedDocument, $actualDocument) * @param array|object $expectedDocument * @param array|object $actualDocument */ - public function assertSameDocument($expectedDocument, $actualDocument) + public function assertSameDocument($expectedDocument, $actualDocument): void { $this->assertEquals( toJSON(fromPHP($this->normalizeBSON($expectedDocument))), @@ -92,7 +92,7 @@ public function assertSameDocument($expectedDocument, $actualDocument) ); } - public function assertSameDocuments(array $expectedDocuments, $actualDocuments) + public function assertSameDocuments(array $expectedDocuments, $actualDocuments): void { if ($actualDocuments instanceof Traversable) { $actualDocuments = iterator_to_array($actualDocuments); @@ -115,7 +115,7 @@ public function assertSameDocuments(array $expectedDocuments, $actualDocuments) /** * Compatibility method as PHPUnit 9 no longer includes this method. */ - public function dataDescription() : string + public function dataDescription(): string { $dataName = $this->dataName(); @@ -132,11 +132,16 @@ public function provideInvalidDocumentValues() return $this->wrapValuesForDataProvider($this->getInvalidDocumentValues()); } - protected function assertDeprecated(callable $execution) + public function provideInvalidIntegerValues() + { + return $this->wrapValuesForDataProvider($this->getInvalidIntegerValues()); + } + + protected function assertDeprecated(callable $execution): void { $errors = []; - set_error_handler(function ($errno, $errstr) use (&$errors) { + set_error_handler(function ($errno, $errstr) use (&$errors): void { $errors[] = $errstr; }, E_USER_DEPRECATED); @@ -151,10 +156,8 @@ protected function assertDeprecated(callable $execution) /** * Return the test collection name. - * - * @return string */ - protected function getCollectionName() + protected function getCollectionName(): string { $class = new ReflectionClass($this); @@ -163,128 +166,89 @@ protected function getCollectionName() /** * Return the test database name. - * - * @return string */ - protected function getDatabaseName() + protected function getDatabaseName(): string { return getenv('MONGODB_DATABASE') ?: 'phplib_test'; } /** * Return a list of invalid array values. - * - * @param boolean $includeNull - * - * @return array */ - protected function getInvalidArrayValues($includeNull = false) + protected function getInvalidArrayValues(bool $includeNull = false): array { return array_merge([123, 3.14, 'foo', true, new stdClass()], $includeNull ? [null] : []); } /** * Return a list of invalid boolean values. - * - * @param boolean $includeNull - * - * @return array */ - protected function getInvalidBooleanValues($includeNull = false) + protected function getInvalidBooleanValues(bool $includeNull = false): array { return array_merge([123, 3.14, 'foo', [], new stdClass()], $includeNull ? [null] : []); } /** * Return a list of invalid document values. - * - * @param boolean $includeNull - * - * @return array */ - protected function getInvalidDocumentValues($includeNull = false) + protected function getInvalidDocumentValues(bool $includeNull = false): array { + // Note: PackedArray is intentionally omitted here (see: PHPLIB-1137) return array_merge([123, 3.14, 'foo', true], $includeNull ? [null] : []); } /** * Return a list of invalid integer values. - * - * @param boolean $includeNull - * - * @return array */ - protected function getInvalidIntegerValues($includeNull = false) + protected function getInvalidIntegerValues(bool $includeNull = false): array { return array_merge([3.14, 'foo', true, [], new stdClass()], $includeNull ? [null] : []); } /** * Return a list of invalid ReadPreference values. - * - * @param boolean $includeNull - * - * @return array */ - protected function getInvalidReadConcernValues($includeNull = false) + protected function getInvalidReadConcernValues(bool $includeNull = false): array { - return array_merge([123, 3.14, 'foo', true, [], new stdClass(), new ReadPreference(ReadPreference::RP_PRIMARY), new WriteConcern(1)], $includeNull ? [null] : []); + return array_merge([123, 3.14, 'foo', true, [], new stdClass(), new ReadPreference(ReadPreference::PRIMARY), new WriteConcern(1)], $includeNull ? [null] : []); } /** * Return a list of invalid ReadPreference values. - * - * @param boolean $includeNull - * - * @return array */ - protected function getInvalidReadPreferenceValues($includeNull = false) + protected function getInvalidReadPreferenceValues(bool $includeNull = false): array { return array_merge([123, 3.14, 'foo', true, [], new stdClass(), new ReadConcern(), new WriteConcern(1)], $includeNull ? [null] : []); } /** * Return a list of invalid Session values. - * - * @param boolean $includeNull - * - * @return array */ - protected function getInvalidSessionValues($includeNull = false) + protected function getInvalidSessionValues(bool $includeNull = false): array { - return array_merge([123, 3.14, 'foo', true, [], new stdClass(), new ReadConcern(), new ReadPreference(ReadPreference::RP_PRIMARY), new WriteConcern(1)], $includeNull ? [null] : []); + return array_merge([123, 3.14, 'foo', true, [], new stdClass(), new ReadConcern(), new ReadPreference(ReadPreference::PRIMARY), new WriteConcern(1)], $includeNull ? [null] : []); } /** * Return a list of invalid string values. - * - * @param boolean $includeNull - * - * @return array */ - protected function getInvalidStringValues($includeNull = false) + protected function getInvalidStringValues(bool $includeNull = false): array { return array_merge([123, 3.14, true, [], new stdClass()], $includeNull ? [null] : []); } /** * Return a list of invalid WriteConcern values. - * - * @param boolean $includeNull - * - * @return array */ - protected function getInvalidWriteConcernValues($includeNull = false) + protected function getInvalidWriteConcernValues(bool $includeNull = false): array { - return array_merge([123, 3.14, 'foo', true, [], new stdClass(), new ReadConcern(), new ReadPreference(ReadPreference::RP_PRIMARY)], $includeNull ? [null] : []); + return array_merge([123, 3.14, 'foo', true, [], new stdClass(), new ReadConcern(), new ReadPreference(ReadPreference::PRIMARY)], $includeNull ? [null] : []); } /** * Return the test namespace. - * - * @return string */ - protected function getNamespace() + protected function getNamespace(): string { return sprintf('%s.%s', $this->getDatabaseName(), $this->getCollectionName()); } @@ -293,9 +257,8 @@ protected function getNamespace() * Wrap a list of values for use as a single-argument data provider. * * @param array $values List of values - * @return array */ - protected function wrapValuesForDataProvider(array $values) + protected function wrapValuesForDataProvider(array $values): array { return array_map(function ($value) { return [$value]; diff --git a/tests/UnifiedSpecTests/CollectionData.php b/tests/UnifiedSpecTests/CollectionData.php index c81615a9f..46d7e2ec2 100644 --- a/tests/UnifiedSpecTests/CollectionData.php +++ b/tests/UnifiedSpecTests/CollectionData.php @@ -3,7 +3,6 @@ namespace MongoDB\Tests\UnifiedSpecTests; use ArrayIterator; -use IteratorIterator; use MongoDB\Client; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadPreference; @@ -11,6 +10,7 @@ use MongoDB\Tests\UnifiedSpecTests\Constraint\Matches; use MultipleIterator; use stdClass; + use function PHPUnit\Framework\assertContainsOnly; use function PHPUnit\Framework\assertIsArray; use function PHPUnit\Framework\assertIsString; @@ -42,7 +42,7 @@ public function __construct(stdClass $o) $this->documents = $o->documents; } - public function prepareInitialData(Client $client) + public function prepareInitialData(Client $client): void { $database = $client->selectDatabase( $this->databaseName, @@ -60,7 +60,7 @@ public function prepareInitialData(Client $client) $database->selectCollection($this->collectionName)->insertMany($this->documents); } - public function assertOutcome(Client $client) + public function assertOutcome(Client $client): void { $collection = $client->selectCollection( $this->databaseName, @@ -75,10 +75,10 @@ public function assertOutcome(Client $client) $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); $mi->attachIterator(new ArrayIterator($this->documents)); - $mi->attachIterator(new IteratorIterator($cursor)); + $mi->attachIterator($cursor); foreach ($mi as $i => $documents) { - list($expectedDocument, $actualDocument) = $documents; + [$expectedDocument, $actualDocument] = $documents; assertNotNull($expectedDocument); assertNotNull($actualDocument); diff --git a/tests/UnifiedSpecTests/Constraint/IsBsonType.php b/tests/UnifiedSpecTests/Constraint/IsBsonType.php index 76dbe4278..16cbdb5bc 100644 --- a/tests/UnifiedSpecTests/Constraint/IsBsonType.php +++ b/tests/UnifiedSpecTests/Constraint/IsBsonType.php @@ -24,6 +24,7 @@ use PHPUnit\Framework\Constraint\LogicalOr; use RuntimeException; use Symfony\Bridge\PhpUnit\ConstraintTrait; + use function array_keys; use function array_map; use function count; @@ -36,7 +37,6 @@ use function is_string; use function range; use function sprintf; -use const PHP_INT_SIZE; final class IsBsonType extends Constraint { @@ -65,6 +65,7 @@ final class IsBsonType extends Constraint 'decimal', 'minKey', 'maxKey', + 'number', ]; /** @var string */ @@ -79,12 +80,12 @@ public function __construct(string $type) $this->type = $type; } - public static function any() : LogicalOr + public static function any(): LogicalOr { return self::anyOf(...self::$types); } - public static function anyOf(string ...$types) : Constraint + public static function anyOf(string ...$types): Constraint { if (count($types) === 1) { return new self(...$types); @@ -95,67 +96,87 @@ public static function anyOf(string ...$types) : Constraint }, $types)); } - private function doMatches($other) : bool + private function doMatches($other): bool { switch ($this->type) { case 'double': return is_float($other); + case 'string': return is_string($other); + case 'object': return self::isObject($other); + case 'array': return self::isArray($other); + case 'binData': return $other instanceof BinaryInterface; + case 'undefined': return $other instanceof Undefined; + case 'objectId': return $other instanceof ObjectIdInterface; + case 'bool': return is_bool($other); + case 'date': return $other instanceof UTCDateTimeInterface; + case 'null': return $other === null; + case 'regex': return $other instanceof RegexInterface; + case 'dbPointer': return $other instanceof DBPointer; + case 'javascript': return $other instanceof JavascriptInterface && $other->getScope() === null; + case 'symbol': return $other instanceof Symbol; + case 'javascriptWithScope': return $other instanceof JavascriptInterface && $other->getScope() !== null; + case 'int': return is_int($other); + case 'timestamp': return $other instanceof TimestampInterface; + case 'long': - if (PHP_INT_SIZE == 4) { - return $other instanceof Int64; - } + return is_int($other) || $other instanceof Int64; - return is_int($other); case 'decimal': return $other instanceof Decimal128Interface; + case 'minKey': return $other instanceof MinKeyInterface; + case 'maxKey': return $other instanceof MaxKeyInterface; + + case 'number': + return is_int($other) || $other instanceof Int64 || is_float($other) || $other instanceof Decimal128Interface; + default: // This should already have been caught in the constructor throw new LogicException('Unsupported type: ' . $this->type); } } - private function doToString() : string + private function doToString(): string { return sprintf('is of BSON type "%s"', $this->type); } - private static function isArray($other) : bool + private static function isArray($other): bool { if ($other instanceof BSONArray) { return true; @@ -174,7 +195,7 @@ private static function isArray($other) : bool return self::isArrayEmptyOrIndexed($other); } - private static function isObject($other) : bool + private static function isObject($other): bool { if ($other instanceof BSONDocument) { return true; @@ -199,7 +220,7 @@ private static function isObject($other) : bool return ! $other instanceof Type; } - private static function isArrayEmptyOrIndexed(array $a) : bool + private static function isArrayEmptyOrIndexed(array $a): bool { if (empty($a)) { return true; diff --git a/tests/UnifiedSpecTests/Constraint/IsBsonTypeTest.php b/tests/UnifiedSpecTests/Constraint/IsBsonTypeTest.php index 302e63c3b..d14b09e60 100644 --- a/tests/UnifiedSpecTests/Constraint/IsBsonTypeTest.php +++ b/tests/UnifiedSpecTests/Constraint/IsBsonTypeTest.php @@ -4,6 +4,7 @@ use MongoDB\BSON\Binary; use MongoDB\BSON\Decimal128; +use MongoDB\BSON\Int64; use MongoDB\BSON\Javascript; use MongoDB\BSON\MaxKey; use MongoDB\BSON\MinKey; @@ -18,27 +19,28 @@ use PHPUnit\Framework\Constraint\Constraint; use PHPUnit\Framework\ExpectationFailedException; use stdClass; + use function fopen; use function MongoDB\BSON\fromJSON; use function MongoDB\BSON\toPHP; -use function unserialize; + use const PHP_INT_SIZE; class IsBsonTypeTest extends TestCase { - /** - * @dataProvider provideTypes - */ - public function testConstraint($type, $value) + /** @dataProvider provideTypes */ + public function testConstraint($type, $value): void { $this->assertResult(true, new IsBsonType($type), $value, $this->dataName() . ' is ' . $type); } public function provideTypes() { - $undefined = toPHP(fromJSON('{ "undefined": {"$undefined": true} }')); - $symbol = toPHP(fromJSON('{ "symbol": {"$symbol": "test"} }')); - $dbPointer = toPHP(fromJSON('{ "dbPointer": {"$dbPointer": {"$ref": "phongo.test", "$id" : { "$oid" : "5a2e78accd485d55b405ac12" } }} }')); + $undefined = toPHP(fromJSON('{ "x": {"$undefined": true} }'))->x; + $symbol = toPHP(fromJSON('{ "x": {"$symbol": "test"} }'))->x; + $dbPointer = toPHP(fromJSON('{ "x": {"$dbPointer": {"$ref": "db.coll", "$id" : { "$oid" : "5a2e78accd485d55b405ac12" } }} }'))->x; + $int64 = new Int64(1); + $long = PHP_INT_SIZE == 4 ? new Int64('4294967296') : 4294967296; return [ 'double' => ['double', 1.4], @@ -46,52 +48,66 @@ public function provideTypes() // Note: additional tests in testTypeObject 'object(stdClass)' => ['object', new stdClass()], 'object(BSONDocument)' => ['object', new BSONDocument()], - // Note: additional tests tests in testTypeArray + // Note: additional tests in testTypeArray 'array(indexed array)' => ['array', ['foo']], 'array(BSONArray)' => ['array', new BSONArray()], 'binData' => ['binData', new Binary('', 0)], - 'undefined' => ['undefined', $undefined->undefined], + 'undefined' => ['undefined', $undefined], 'objectId' => ['objectId', new ObjectId()], 'bool' => ['bool', true], 'date' => ['date', new UTCDateTime()], 'null' => ['null', null], 'regex' => ['regex', new Regex('.*')], - 'dbPointer' => ['dbPointer', $dbPointer->dbPointer], + 'dbPointer' => ['dbPointer', $dbPointer], 'javascript' => ['javascript', new Javascript('foo = 1;')], - 'symbol' => ['symbol', $symbol->symbol], + 'symbol' => ['symbol', $symbol], 'javascriptWithScope' => ['javascriptWithScope', new Javascript('foo = 1;', ['x' => 1])], 'int' => ['int', 1], 'timestamp' => ['timestamp', new Timestamp(0, 0)], - 'long' => ['long', PHP_INT_SIZE == 4 ? unserialize('C:18:"MongoDB\BSON\Int64":38:{a:1:{s:7:"integer";s:10:"4294967296";}}') : 4294967296], + 'long(int64)' => ['long', $int64], + 'long(long)' => ['long', $long], 'decimal' => ['decimal', new Decimal128('18446744073709551616')], 'minKey' => ['minKey', new MinKey()], 'maxKey' => ['maxKey', new MaxKey()], + 'number(double)' => ['number', 1.4], + 'number(decimal)' => ['number', new Decimal128('18446744073709551616')], + 'number(int)' => ['number', 1], + 'number(int64)' => ['number', $int64], + 'number(long)' => ['number', $long], ]; } - /** - * @dataProvider provideTypes - */ - public function testAny($type, $value) + /** @dataProvider provideTypes */ + public function testAny($type, $value): void { $this->assertResult(true, IsBsonType::any(), $value, $this->dataName() . ' is a BSON type'); } - public function testAnyExcludesStream() + public function testAnyExcludesStream(): void { $this->assertResult(false, IsBsonType::any(), fopen('php://temp', 'w+b'), 'stream is not a BSON type'); } - public function testAnyOf() + public function testAnyOf(): void { $c = IsBsonType::anyOf('double', 'int'); $this->assertResult(true, $c, 1, 'int is double or int'); - $this->assertResult(true, $c, 1.4, 'int is double or int'); + $this->assertResult(true, $c, 1.4, 'double is double or int'); $this->assertResult(false, $c, 'foo', 'string is not double or int'); } - public function testErrorMessage() + public function testAnyOfWithNumberAlias(): void + { + $c = IsBsonType::anyOf('number', 'string'); + + $this->assertResult(true, $c, 1, 'int is number or string'); + $this->assertResult(true, $c, 1.4, 'double is number or string'); + $this->assertResult(true, $c, 'foo', 'string is number or string'); + $this->assertResult(false, $c, true, 'bool is not number or string'); + } + + public function testErrorMessage(): void { $c = new IsBsonType('string'); @@ -103,7 +119,7 @@ public function testErrorMessage() } } - public function testTypeArray() + public function testTypeArray(): void { $c = new IsBsonType('array'); @@ -118,7 +134,7 @@ public function testTypeArray() $this->assertResult(false, $c, new SerializableObject(), 'SerializableObject is not array'); } - public function testTypeObject() + public function testTypeObject(): void { $c = new IsBsonType('object'); @@ -135,7 +151,7 @@ public function testTypeObject() $this->assertResult(false, $c, new ObjectId(), 'Type other than Serializable is not object'); } - public function testTypeJavascript() + public function testTypeJavascript(): void { $c = new IsBsonType('javascript'); @@ -143,7 +159,7 @@ public function testTypeJavascript() $this->assertResult(false, $c, new Javascript('foo = 1;', ['x' => 1]), 'javascriptWithScope is not javascript'); } - public function testTypeJavascriptWithScope() + public function testTypeJavascriptWithScope(): void { $c = new IsBsonType('javascriptWithScope'); @@ -151,7 +167,7 @@ public function testTypeJavascriptWithScope() $this->assertResult(false, $c, new Javascript('foo = 1;'), 'javascript is not javascriptWithScope'); } - private function assertResult($expected, Constraint $constraint, $value, string $message = '') + private function assertResult($expected, Constraint $constraint, $value, string $message = ''): void { $this->assertSame($expected, $constraint->evaluate($value, '', true), $message); } @@ -161,7 +177,7 @@ private function assertResult($expected, Constraint $constraint, $value, string // phpcs:disable Squiz.Classes.ClassFileName.NoMatch class SerializableArray implements Serializable { - public function bsonSerialize() + public function bsonSerialize(): array { return ['foo']; } @@ -169,7 +185,7 @@ public function bsonSerialize() class SerializableObject implements Serializable { - public function bsonSerialize() + public function bsonSerialize(): array { return ['x' => 1]; } diff --git a/tests/UnifiedSpecTests/Constraint/Matches.php b/tests/UnifiedSpecTests/Constraint/Matches.php index 78bd504c4..4f1586625 100644 --- a/tests/UnifiedSpecTests/Constraint/Matches.php +++ b/tests/UnifiedSpecTests/Constraint/Matches.php @@ -14,6 +14,7 @@ use SebastianBergmann\Comparator\ComparisonFailure; use SebastianBergmann\Comparator\Factory; use Symfony\Bridge\PhpUnit\ConstraintTrait; + use function array_keys; use function count; use function get_debug_type; @@ -23,6 +24,7 @@ use function is_float; use function is_int; use function is_object; +use function ltrim; use function PHPUnit\Framework\assertIsBool; use function PHPUnit\Framework\assertIsString; use function PHPUnit\Framework\assertMatchesRegularExpression; @@ -36,7 +38,7 @@ use function range; use function sprintf; use function strpos; -use const PHP_INT_SIZE; +use function strrchr; /** * Constraint that checks if one value matches another. @@ -64,7 +66,10 @@ class Matches extends Constraint /** @var ComparisonFailure|null */ private $lastFailure; - public function __construct($value, EntityMap $entityMap = null, $allowExtraRootKeys = true, $allowOperators = true) + /** @var Factory */ + private $comparatorFactory; + + public function __construct($value, ?EntityMap $entityMap = null, $allowExtraRootKeys = true, $allowOperators = true) { $this->value = self::prepare($value); $this->entityMap = $entityMap; @@ -110,7 +115,7 @@ private function doEvaluate($other, $description = '', $returnResult = false) } } - private function assertEquals($expected, $actual, string $keyPath) + private function assertEquals($expected, $actual, string $keyPath): void { $expectedType = get_debug_type($expected); $actualType = get_debug_type($actual); @@ -131,7 +136,7 @@ private function assertEquals($expected, $actual, string $keyPath) } } - private function assertMatches($expected, $actual, string $keyPath = '') + private function assertMatches($expected, $actual, string $keyPath = ''): void { if ($expected instanceof BSONArray) { $this->assertMatchesArray($expected, $actual, $keyPath); @@ -148,7 +153,7 @@ private function assertMatches($expected, $actual, string $keyPath = '') $this->assertEquals($expected, $actual, $keyPath); } - private function assertMatchesArray(BSONArray $expected, $actual, string $keyPath) + private function assertMatchesArray(BSONArray $expected, $actual, string $keyPath): void { if (! $actual instanceof BSONArray) { $actualType = get_debug_type($actual); @@ -168,7 +173,7 @@ private function assertMatchesArray(BSONArray $expected, $actual, string $keyPat } } - private function assertMatchesDocument(BSONDocument $expected, $actual, string $keyPath) + private function assertMatchesDocument(BSONDocument $expected, $actual, string $keyPath): void { if ($this->allowOperators && self::isOperator($expected)) { $this->assertMatchesOperator($expected, $actual, $keyPath); @@ -226,6 +231,7 @@ private function assertMatchesDocument(BSONDocument $expected, $actual, string $ return; } + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.NotCamelCaps foreach ($actual as $key => $_) { if (! $expected->offsetExists($key)) { self::failAt(sprintf('$actual has unexpected key "%s"', $key), $keyPath); @@ -233,10 +239,23 @@ private function assertMatchesDocument(BSONDocument $expected, $actual, string $ } } - private function assertMatchesOperator(BSONDocument $operator, $actual, string $keyPath) + private function assertMatchesOperator(BSONDocument $operator, $actual, string $keyPath): void { $name = self::getOperatorName($operator); + if ($name === '$$exists') { + assertIsBool($operator['$$exists'], '$$exists requires bool'); + + /* If we get to this point, the field itself must already exist so + * we need only fail if $$exists is false. */ + if ($operator['$$exists'] === false) { + $key = ltrim(strrchr($keyPath, '.'), '.'); + self::failAt(sprintf('$actual has unexpected key "%s"', $key), $keyPath); + } + + return; + } + if ($name === '$$type') { assertThat( $operator['$$type'], @@ -347,15 +366,17 @@ private function doToString() return 'matches ' . $this->exporter()->export($this->value); } - private static function failAt(string $message, string $keyPath) + /** @psalm-return never-return */ + private static function failAt(string $message, string $keyPath): void { $prefix = empty($keyPath) ? '' : sprintf('Field path "%s": ', $keyPath); throw new RuntimeException($prefix . $message); } - private static function getOperatorName(BSONDocument $document) : string + private static function getOperatorName(BSONDocument $document): string { + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.NotCamelCaps foreach ($document as $key => $_) { if (strpos((string) $key, '$$') === 0) { return $key; @@ -370,12 +391,13 @@ private static function isNumeric($value) return is_int($value) || is_float($value) || $value instanceof Int64; } - private static function isOperator(BSONDocument $document) : bool + private static function isOperator(BSONDocument $document): bool { if (count($document) !== 1) { return false; } + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.NotCamelCaps foreach ($document as $key => $_) { return strpos((string) $key, '$$') === 0; } @@ -401,17 +423,6 @@ private static function prepare($bson) return $bson; } - /* Convert Int64 objects to integers on 64-bit platforms for - * compatibility reasons. */ - if ($bson instanceof Int64 && PHP_INT_SIZE != 4) { - return (int) ((string) $bson); - } - - /* TODO: Convert Int64 objects to integers on 32-bit platforms if they - * can be expressed as such. This is necessary to handle flexible - * numeric comparisons if the server returns 32-bit value as a 64-bit - * integer (e.g. cursor ID). */ - // Serializable can produce an array or object, so recurse on its output if ($bson instanceof Serializable) { return self::prepare($bson->bsonSerialize()); @@ -442,7 +453,7 @@ private static function prepare($bson) return $bson; } - private static function isArrayEmptyOrIndexed(array $a) : bool + private static function isArrayEmptyOrIndexed(array $a): bool { if (empty($a)) { return true; diff --git a/tests/UnifiedSpecTests/Constraint/MatchesTest.php b/tests/UnifiedSpecTests/Constraint/MatchesTest.php index 397894011..53a7643a4 100644 --- a/tests/UnifiedSpecTests/Constraint/MatchesTest.php +++ b/tests/UnifiedSpecTests/Constraint/MatchesTest.php @@ -7,13 +7,13 @@ use MongoDB\Tests\UnifiedSpecTests\EntityMap; use PHPUnit\Framework\ExpectationFailedException; use stdClass; + use function hex2bin; use function preg_quote; -use function version_compare; class MatchesTest extends FunctionalTestCase { - public function testMatchesDocument() + public function testMatchesDocument(): void { $c = new Matches(['x' => 1, 'y' => ['a' => 1, 'b' => 2]]); $this->assertResult(false, $c, ['x' => 1, 'y' => 2], 'Incorrect value'); @@ -23,20 +23,28 @@ public function testMatchesDocument() $this->assertResult(true, $c, ['y' => ['b' => 2, 'a' => 1], 'x' => 1], 'Root and embedded key order is not significant'); } - public function testDoNotAllowExtraRootKeys() + public function testFlexibleNumericComparison(): void + { + $c = new Matches(['x' => 1, 'y' => 1.0]); + $this->assertResult(true, $c, ['x' => 1.0, 'y' => 1.0], 'Float instead of expected int matches'); + $this->assertResult(true, $c, ['x' => 1, 'y' => 1], 'Int instead of expected float matches'); + $this->assertResult(false, $c, ['x' => 'foo', 'y' => 1.0], 'Different type does not match'); + } + + public function testDoNotAllowExtraRootKeys(): void { $c = new Matches(['x' => 1], null, false); $this->assertResult(false, $c, ['x' => 1, 'y' => 1], 'Extra keys in root are prohibited'); } - public function testDoNotAllowOperators() + public function testDoNotAllowOperators(): void { $c = new Matches(['x' => ['$$exists' => true]], null, true, false); $this->assertResult(false, $c, ['x' => 1], 'Operators are not processed'); $this->assertResult(true, $c, ['x' => ['$$exists' => true]], 'Operators are not processed but compared as-is'); } - public function testOperatorExists() + public function testOperatorExists(): void { $c = new Matches(['x' => ['$$exists' => true]]); $this->assertResult(true, $c, ['x' => '1'], 'root-level key exists'); @@ -59,7 +67,7 @@ public function testOperatorExists() $this->assertResult(true, $c, ['x' => new stdClass()], 'embedded key missing'); } - public function testOperatorType() + public function testOperatorType(): void { $c = new Matches(['x' => ['$$type' => 'string']]); $this->assertResult(true, $c, ['x' => 'foo'], 'string matches string type'); @@ -71,7 +79,7 @@ public function testOperatorType() $this->assertResult(false, $c, ['x' => 1], 'integer does not match [string,bool] type'); } - public function testOperatorMatchesEntity() + public function testOperatorMatchesEntity(): void { $entityMap = new EntityMap(); $entityMap->set('integer', 1); @@ -93,7 +101,7 @@ public function testOperatorMatchesEntity() $this->assertResult(false, $c, ['x' => ['y' => 1, 'z' => 2]], 'value does not match object entity (root-level)'); } - public function testOperatorMatchesHexBytes() + public function testOperatorMatchesHexBytes(): void { $c = new Matches(['$$matchesHexBytes' => 'DEADBEEF']); $this->assertResult(true, $c, hex2bin('DEADBEEF'), 'value matches hex bytes (root-level)'); @@ -104,7 +112,7 @@ public function testOperatorMatchesHexBytes() $this->assertResult(false, $c, ['x' => hex2bin('DEADBEEF')], 'value does not match hex bytes (embedded)'); } - public function testOperatorUnsetOrMatches() + public function testOperatorUnsetOrMatches(): void { $c = new Matches(['$$unsetOrMatches' => ['x' => 1]]); $this->assertResult(true, $c, null, 'null value is considered unset (root-level)'); @@ -119,12 +127,28 @@ public function testOperatorUnsetOrMatches() $this->assertResult(false, $c, ['x' => ['y' => 1, 'z' => 2]], 'value does not match (embedded)'); } - public function testOperatorSessionLsid() + public function testOperatorUnsetOrMatchesWithNestedOperator(): void { - if (version_compare($this->getFeatureCompatibilityVersion(), '3.6', '<')) { - $this->markTestSkipped('startSession() is only supported on FCV 3.6 or higher'); - } + // Nested $$unsetOrMatches is redundant, but should behave the same as if it was omitted + $c = new Matches(['x' => ['$$unsetOrMatches' => ['$$unsetOrMatches' => ['y' => 1]]]]); + $this->assertResult(true, $c, new stdClass(), 'missing value is considered unset (embedded)'); + $this->assertResult(false, $c, ['x' => null], 'null value is not considered unset (embedded)'); + $this->assertResult(true, $c, ['x' => ['y' => 1]], 'value matches (embedded)'); + $this->assertResult(false, $c, ['x' => ['y' => 1, 'z' => 2]], 'value does not match (embedded)'); + + $c = new Matches(['x' => ['$$unsetOrMatches' => ['$$exists' => true]]]); + $this->assertResult(true, $c, new stdClass(), 'missing value is considered unset (embedded)'); + $this->assertResult(true, $c, ['x' => null], 'null value is not considered unset (embedded)'); + $this->assertResult(true, $c, ['x' => ['y' => 1]], 'non-null value is not considered unset (embedded)'); + + $c = new Matches(['x' => ['$$unsetOrMatches' => ['$$exists' => false]]]); + $this->assertResult(true, $c, new stdClass(), 'missing value is considered unset (embedded)'); + $this->assertResult(false, $c, ['x' => null], 'null value is not considered unset (embedded)'); + $this->assertResult(false, $c, ['x' => ['y' => 1]], 'non-null value is not considered unset (embedded)'); + } + public function testOperatorSessionLsid(): void + { $session = $this->manager->startSession(); $entityMap = new EntityMap(); @@ -146,10 +170,8 @@ public function testOperatorSessionLsid() $this->assertResult(false, $c, ['x' => 1], 'session LSID does not match (embedded)'); } - /** - * @dataProvider errorMessageProvider - */ - public function testErrorMessages($expectedMessageRegex, Matches $constraint, $actualValue) + /** @dataProvider errorMessageProvider */ + public function testErrorMessages($expectedMessageRegex, Matches $constraint, $actualValue): void { try { $constraint->evaluate($actualValue); @@ -231,10 +253,8 @@ public function errorMessageProvider() ]; } - /** - * @dataProvider operatorErrorMessageProvider - */ - public function testOperatorSyntaxValidation($expectedMessage, Matches $constraint) + /** @dataProvider operatorErrorMessageProvider */ + public function testOperatorSyntaxValidation($expectedMessage, Matches $constraint): void { $this->expectException(ExpectationFailedException::class); $this->expectExceptionMessage($expectedMessage); @@ -284,7 +304,7 @@ public function operatorErrorMessageProvider() ]; } - private function assertResult($expected, Matches $constraint, $value, string $message = '') + private function assertResult($expected, Matches $constraint, $value, string $message = ''): void { $this->assertSame($expected, $constraint->evaluate($value, '', true), $message); } diff --git a/tests/UnifiedSpecTests/Context.php b/tests/UnifiedSpecTests/Context.php index b62a5a733..2300965f3 100644 --- a/tests/UnifiedSpecTests/Context.php +++ b/tests/UnifiedSpecTests/Context.php @@ -4,19 +4,20 @@ use LogicException; use MongoDB\Client; -use MongoDB\Driver\Manager; -use MongoDB\Driver\ReadPreference; -use MongoDB\Driver\Server; +use MongoDB\Driver\ClientEncryption; +use MongoDB\Driver\ServerApi; +use MongoDB\Model\BSONArray; +use MongoDB\Tests\FunctionalTestCase; +use MongoDB\Tests\SpecTests\ClientSideEncryptionSpecTest; +use PHPUnit\Framework\Assert; use stdClass; + use function array_key_exists; use function array_map; -use function count; use function current; use function explode; -use function implode; -use function in_array; +use function getenv; use function key; -use function parse_url; use function PHPUnit\Framework\assertArrayHasKey; use function PHPUnit\Framework\assertCount; use function PHPUnit\Framework\assertIsArray; @@ -25,13 +26,9 @@ use function PHPUnit\Framework\assertIsObject; use function PHPUnit\Framework\assertIsString; use function PHPUnit\Framework\assertNotEmpty; -use function PHPUnit\Framework\assertNotFalse; -use function PHPUnit\Framework\assertStringContainsString; -use function PHPUnit\Framework\assertStringStartsWith; -use function strlen; -use function strpos; -use function substr_replace; -use const PHP_URL_HOST; +use function PHPUnit\Framework\assertNotSame; +use function PHPUnit\Framework\assertSame; +use function sprintf; /** * Execution context for spec tests. @@ -44,34 +41,58 @@ final class Context /** @var string */ private $activeClient; - /** @var string[] */ - private $dirtySessions = []; - /** @var EntityMap */ private $entityMap; + /** @var EventCollector[] */ + private $eventCollectors = []; + /** @var EventObserver[] */ private $eventObserversByClient = []; /** @var Client */ private $internalClient; + /** @var boolean */ + private $inLoop = false; + /** @var string */ private $uri; + /** @var string */ + private $singleMongosUri; + + /** @var string */ + private $multiMongosUri; + public function __construct(Client $internalClient, string $uri) { $this->entityMap = new EntityMap(); $this->internalClient = $internalClient; $this->uri = $uri; + + /* TODO: Consider leaving these unset, although that might require + * redundant topology/serverless checks in Context::createClient(). */ + $this->singleMongosUri = $uri; + $this->multiMongosUri = $uri; } /** - * Create entities for "createEntities". + * Set single and multi-mongos URIs for useMultipleMongoses. This should be + * called for sharded cluster and non-serverless load balanced topologies. * - * @param array $createEntities + * @see UnifiedTestRunner::createContext() + */ + public function setUrisForUseMultipleMongoses(string $singleMongosUri, string $multiMongosUri): void + { + $this->singleMongosUri = $singleMongosUri; + $this->multiMongosUri = $multiMongosUri; + } + + /** + * Create entities for "createEntities". */ - public function createEntities(array $entities) + public function createEntities(array $entities): void { foreach ($entities as $entity) { assertIsObject($entity); @@ -90,6 +111,10 @@ public function createEntities(array $entities) $this->createClient($id, $def); break; + case 'clientEncryption': + $this->createClientEncryption($id, $def); + break; + case 'database': $this->createDatabase($id, $def); break; @@ -112,90 +137,105 @@ public function createEntities(array $entities) } } - public function getEntityMap() : EntityMap + public function getEntityMap(): EntityMap { return $this->entityMap; } - public function getInternalClient() : Client + public function getInternalClient(): Client { return $this->internalClient; } - public function isDirtySession(string $sessionId) : bool + public function isActiveClient(string $clientId): bool { - return in_array($sessionId, $this->dirtySessions); + return $this->activeClient === $clientId; } - public function markDirtySession(string $sessionId) + public function setActiveClient(?string $clientId = null): void { - if ($this->isDirtySession($sessionId)) { - return; - } - - $this->dirtySessions[] = $sessionId; + $this->activeClient = $clientId; } - public function isActiveClient(string $clientId) : bool + public function isInLoop(): bool { - return $this->activeClient === $clientId; + return $this->inLoop; } - public function setActiveClient(string $clientId = null) + public function setInLoop(bool $inLoop): void { - $this->activeClient = $clientId; + $this->inLoop = $inLoop; } - public function assertExpectedEventsForClients(array $expectedEventsForClients) + public function assertExpectedEventsForClients(array $expectedEventsForClients): void { assertNotEmpty($expectedEventsForClients); foreach ($expectedEventsForClients as $expectedEventsForClient) { assertIsObject($expectedEventsForClient); - Util::assertHasOnlyKeys($expectedEventsForClient, ['client', 'events']); + Util::assertHasOnlyKeys($expectedEventsForClient, ['client', 'events', 'eventType', 'ignoreExtraEvents']); $client = $expectedEventsForClient->client ?? null; + $eventType = $expectedEventsForClient->eventType ?? 'command'; $expectedEvents = $expectedEventsForClient->events ?? null; + $ignoreExtraEvents = $expectedEventsForClient->ignoreExtraEvents ?? false; assertIsString($client); assertArrayHasKey($client, $this->eventObserversByClient); + /* Note: PHPC does not implement CMAP. Any tests expecting CMAP + * events should be skipped explicitly. */ + assertSame('command', $eventType); assertIsArray($expectedEvents); - $this->eventObserversByClient[$client]->assert($expectedEvents); + $this->eventObserversByClient[$client]->assert($expectedEvents, $ignoreExtraEvents); } } - public function startEventObservers() + public function startEventObservers(): void { foreach ($this->eventObserversByClient as $eventObserver) { $eventObserver->start(); } } - public function stopEventObservers() + public function stopEventObservers(): void { foreach ($this->eventObserversByClient as $eventObserver) { $eventObserver->stop(); } } - public function getEventObserverForClient(string $id) : EventObserver + public function getEventObserverForClient(string $id): EventObserver { assertArrayHasKey($id, $this->eventObserversByClient); return $this->eventObserversByClient[$id]; } + public function startEventCollectors(): void + { + foreach ($this->eventCollectors as $eventCollector) { + $eventCollector->start(); + } + } + + public function stopEventCollectors(): void + { + foreach ($this->eventCollectors as $eventCollector) { + $eventCollector->stop(); + } + } + /** @param string|array $readPreferenceTags */ - private function convertReadPreferenceTags($readPreferenceTags) : array + private function convertReadPreferenceTags($readPreferenceTags): array { return array_map( - static function (string $readPreferenceTagSet) : array { + static function (string $readPreferenceTagSet): array { $tags = explode(',', $readPreferenceTagSet); return array_map( - static function (string $tag) : array { - list($key, $value) = explode(':', $tag); + static function (string $tag): array { + [$key, $value] = explode(':', $tag); return [$key => $value]; }, @@ -206,24 +246,32 @@ static function (string $tag) : array { ); } - private function createClient(string $id, stdClass $o) + private function createClient(string $id, stdClass $o): void { - Util::assertHasOnlyKeys($o, ['id', 'uriOptions', 'useMultipleMongoses', 'observeEvents', 'ignoreCommandMonitoringEvents']); + Util::assertHasOnlyKeys($o, [ + 'id', + 'uriOptions', + 'useMultipleMongoses', + 'observeEvents', + 'ignoreCommandMonitoringEvents', + 'observeSensitiveCommands', + 'serverApi', + 'storeEventsAsEntities', + ]); $useMultipleMongoses = $o->useMultipleMongoses ?? null; $observeEvents = $o->observeEvents ?? null; $ignoreCommandMonitoringEvents = $o->ignoreCommandMonitoringEvents ?? []; + $observeSensitiveCommands = $o->observeSensitiveCommands ?? false; + $serverApi = $o->serverApi ?? null; + $storeEventsAsEntities = $o->storeEventsAsEntities ?? null; $uri = $this->uri; if (isset($useMultipleMongoses)) { assertIsBool($useMultipleMongoses); - if ($useMultipleMongoses) { - self::requireMultipleMongoses($uri); - } else { - $uri = self::removeMultipleMongoses($uri); - } + $uri = $useMultipleMongoses ? $this->multiMongosUri : $this->singleMongosUri; } $uriOptions = []; @@ -247,19 +295,123 @@ private function createClient(string $id, stdClass $o) if (isset($observeEvents)) { assertIsArray($observeEvents); assertIsArray($ignoreCommandMonitoringEvents); + assertIsBool($observeSensitiveCommands); + + $this->eventObserversByClient[$id] = new EventObserver($observeEvents, $ignoreCommandMonitoringEvents, $observeSensitiveCommands, $id, $this); + } + + if (isset($storeEventsAsEntities)) { + assertIsArray($storeEventsAsEntities); + + foreach ($storeEventsAsEntities as $storeEventsAsEntity) { + $this->createEntityCollector($id, $storeEventsAsEntity); + } + } + + // Transaction tests expect a new client for each test so that txnNumber values are deterministic. + $driverOptions = isset($observeEvents) ? ['disableClientPersistence' => true] : []; + + if ($serverApi !== null) { + assertIsObject($serverApi); + $driverOptions['serverApi'] = new ServerApi( + $serverApi->version, + $serverApi->strict ?? null, + $serverApi->deprecationErrors ?? null + ); + } + + $this->entityMap->set($id, FunctionalTestCase::createTestClient($uri, $uriOptions, $driverOptions)); + } + + private function createClientEncryption(string $id, stdClass $o): void + { + Util::assertHasOnlyKeys($o, [ + 'id', + 'clientEncryptionOpts', + ]); + + $clientEncryptionOpts = []; + $clientId = null; + + if (isset($o->clientEncryptionOpts)) { + assertIsObject($o->clientEncryptionOpts); + $clientEncryptionOpts = (array) $o->clientEncryptionOpts; + } + + if (isset($clientEncryptionOpts['keyVaultClient'])) { + assertIsString($clientEncryptionOpts['keyVaultClient']); + /* Record the keyVaultClient's ID, which we'll later use to track + * the parent client in the entity map. */ + $clientId = $clientEncryptionOpts['keyVaultClient']; + $clientEncryptionOpts['keyVaultClient'] = $this->entityMap->getClient($clientId)->getManager(); + } + + if (isset($clientEncryptionOpts['kmsProviders'])) { + assertIsObject($clientEncryptionOpts['kmsProviders']); + + if (isset($clientEncryptionOpts['kmsProviders']->aws->accessKeyId->{'$$placeholder'})) { + $clientEncryptionOpts['kmsProviders']->aws->accessKeyId = static::getEnv('AWS_ACCESS_KEY_ID'); + } + + if (isset($clientEncryptionOpts['kmsProviders']->aws->secretAccessKey->{'$$placeholder'})) { + $clientEncryptionOpts['kmsProviders']->aws->secretAccessKey = static::getEnv('AWS_SECRET_ACCESS_KEY'); + } - $this->eventObserversByClient[$id] = new EventObserver($observeEvents, $ignoreCommandMonitoringEvents, $id, $this); + if (isset($clientEncryptionOpts['kmsProviders']->azure->clientId->{'$$placeholder'})) { + $clientEncryptionOpts['kmsProviders']->azure->clientId = static::getEnv('AZURE_CLIENT_ID'); + } + + if (isset($clientEncryptionOpts['kmsProviders']->azure->clientSecret->{'$$placeholder'})) { + $clientEncryptionOpts['kmsProviders']->azure->clientSecret = static::getEnv('AZURE_CLIENT_SECRET'); + } + + if (isset($clientEncryptionOpts['kmsProviders']->azure->tenantId->{'$$placeholder'})) { + $clientEncryptionOpts['kmsProviders']->azure->tenantId = static::getEnv('AZURE_TENANT_ID'); + } + + if (isset($clientEncryptionOpts['kmsProviders']->gcp->email->{'$$placeholder'})) { + $clientEncryptionOpts['kmsProviders']->gcp->email = static::getEnv('GCP_EMAIL'); + } + + if (isset($clientEncryptionOpts['kmsProviders']->gcp->privateKey->{'$$placeholder'})) { + $clientEncryptionOpts['kmsProviders']->gcp->privateKey = static::getEnv('GCP_PRIVATE_KEY'); + } + + if (isset($clientEncryptionOpts['kmsProviders']->kmip->endpoint->{'$$placeholder'})) { + $clientEncryptionOpts['kmsProviders']->kmip->endpoint = static::getEnv('KMIP_ENDPOINT'); + } + + if (isset($clientEncryptionOpts['kmsProviders']->local->key->{'$$placeholder'})) { + $clientEncryptionOpts['kmsProviders']->local->key = ClientSideEncryptionSpecTest::LOCAL_MASTERKEY; + } + } + + if (isset($clientEncryptionOpts['kmsProviders']->kmip->endpoint)) { + $clientEncryptionOpts['tlsOptions']['kmip'] = [ + 'tlsCAFile' => static::getEnv('KMS_TLS_CA_FILE'), + 'tlsCertificateKeyFile' => static::getEnv('KMS_TLS_CERTIFICATE_KEY_FILE'), + ]; } - /* TODO: Remove this once PHPC-1645 is implemented. Each client needs - * its own libmongoc client to facilitate txnNumber assertions. */ - static $i = 0; - $driverOptions = isset($observeEvents) ? ['i' => $i++] : []; + $this->entityMap->set($id, new ClientEncryption($clientEncryptionOpts), $clientId); + } + + private function createEntityCollector(string $clientId, stdClass $o): void + { + Util::assertHasOnlyKeys($o, ['id', 'events']); - $this->entityMap->set($id, new Client($uri, $uriOptions, $driverOptions)); + $eventListId = $o->id ?? null; + $events = $o->events ?? null; + + assertNotSame($eventListId, $clientId); + assertIsArray($events); + + $eventList = new BSONArray(); + $this->entityMap->set($eventListId, $eventList); + $this->eventCollectors[] = new EventCollector($eventList, $events, $clientId, $this); } - private function createCollection(string $id, stdClass $o) + private function createCollection(string $id, stdClass $o): void { Util::assertHasOnlyKeys($o, ['id', 'database', 'collectionName', 'collectionOptions']); @@ -281,7 +433,7 @@ private function createCollection(string $id, stdClass $o) $this->entityMap->set($id, $database->selectCollection($o->collectionName, $options), $databaseId); } - private function createDatabase(string $id, stdClass $o) + private function createDatabase(string $id, stdClass $o): void { Util::assertHasOnlyKeys($o, ['id', 'client', 'databaseName', 'databaseOptions']); @@ -303,7 +455,7 @@ private function createDatabase(string $id, stdClass $o) $this->entityMap->set($id, $client->selectDatabase($databaseName, $options), $clientId); } - private function createSession(string $id, stdClass $o) + private function createSession(string $id, stdClass $o): void { Util::assertHasOnlyKeys($o, ['id', 'client', 'sessionOptions']); @@ -321,7 +473,7 @@ private function createSession(string $id, stdClass $o) $this->entityMap->set($id, $client->startSession($options), $clientId); } - private function createBucket(string $id, stdClass $o) + private function createBucket(string $id, stdClass $o): void { Util::assertHasOnlyKeys($o, ['id', 'database', 'bucketOptions']); @@ -339,14 +491,25 @@ private function createBucket(string $id, stdClass $o) $this->entityMap->set($id, $database->selectGridFSBucket($options), $databaseId); } - private static function prepareCollectionOrDatabaseOptions(array $options) : array + private static function getEnv(string $name): string + { + $value = getenv($name); + + if ($value === false) { + Assert::markTestSkipped(sprintf('Environment variable "%s" is not defined', $name)); + } + + return $value; + } + + private static function prepareCollectionOrDatabaseOptions(array $options): array { Util::assertHasOnlyKeys($options, ['readConcern', 'readPreference', 'writeConcern']); return Util::prepareCommonOptions($options); } - private static function prepareBucketOptions(array $options) : array + private static function prepareBucketOptions(array $options): array { Util::assertHasOnlyKeys($options, ['bucketName', 'chunkSizeBytes', 'disableMD5', 'readConcern', 'readPreference', 'writeConcern']); @@ -365,9 +528,9 @@ private static function prepareBucketOptions(array $options) : array return Util::prepareCommonOptions($options); } - private static function prepareSessionOptions(array $options) : array + private static function prepareSessionOptions(array $options): array { - Util::assertHasOnlyKeys($options, ['causalConsistency', 'defaultTransactionOptions']); + Util::assertHasOnlyKeys($options, ['causalConsistency', 'defaultTransactionOptions', 'snapshot']); if (array_key_exists('causalConsistency', $options)) { assertIsBool($options['causalConsistency']); @@ -378,10 +541,14 @@ private static function prepareSessionOptions(array $options) : array $options['defaultTransactionOptions'] = self::prepareDefaultTransactionOptions((array) $options['defaultTransactionOptions']); } + if (array_key_exists('snapshot', $options)) { + assertIsBool($options['snapshot']); + } + return $options; } - private static function prepareDefaultTransactionOptions(array $options) : array + private static function prepareDefaultTransactionOptions(array $options): array { Util::assertHasOnlyKeys($options, ['maxCommitTimeMS', 'readConcern', 'readPreference', 'writeConcern']); @@ -391,62 +558,4 @@ private static function prepareDefaultTransactionOptions(array $options) : array return Util::prepareCommonOptions($options); } - - /** - * Removes mongos hosts beyond the first if the URI refers to a sharded - * cluster. Otherwise, the URI is returned as-is. - */ - private static function removeMultipleMongoses(string $uri) : string - { - assertStringStartsWith('mongodb://', $uri); - - $manager = new Manager($uri); - - // Nothing to do if the URI does not refer to a sharded cluster - if ($manager->selectServer(new ReadPreference(ReadPreference::PRIMARY))->getType() !== Server::TYPE_MONGOS) { - return $uri; - } - - $parts = parse_url($uri); - - assertIsArray($parts); - - $hosts = explode(',', $parts['host']); - - // Nothing to do if the URI already has a single mongos host - if (count($hosts) === 1) { - return $uri; - } - - // Re-append port to last host - if (isset($parts['port'])) { - $hosts[count($hosts) - 1] .= ':' . $parts['port']; - } - - $singleHost = $hosts[0]; - $multipleHosts = implode(',', $hosts); - - $pos = strpos($uri, $multipleHosts); - - assertNotFalse($pos); - - return substr_replace($uri, $singleHost, $pos, strlen($multipleHosts)); - } - - /** - * Requires multiple mongos hosts if the URI refers to a sharded cluster. - */ - private static function requireMultipleMongoses(string $uri) - { - assertStringStartsWith('mongodb://', $uri); - - $manager = new Manager($uri); - - // Nothing to do if the URI does not refer to a sharded cluster - if ($manager->selectServer(new ReadPreference(ReadPreference::PRIMARY))->getType() !== Server::TYPE_MONGOS) { - return; - } - - assertStringContainsString(',', parse_url($uri, PHP_URL_HOST)); - } } diff --git a/tests/UnifiedSpecTests/DirtySessionObserver.php b/tests/UnifiedSpecTests/DirtySessionObserver.php deleted file mode 100644 index e69f7e534..000000000 --- a/tests/UnifiedSpecTests/DirtySessionObserver.php +++ /dev/null @@ -1,83 +0,0 @@ -lsid = $lsid; - } - - /** - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php - */ - public function commandFailed(CommandFailedEvent $event) - { - if (! in_array($event->getRequestId(), $this->requestIds)) { - return; - } - - if ($event->getError() instanceof ConnectionException) { - $this->observedNetworkError = true; - } - } - - /** - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php - */ - public function commandStarted(CommandStartedEvent $event) - { - if ($this->lsid == ($event->getCommand()->lsid ?? null)) { - $this->requestIds[] = $event->getRequestId(); - } - } - - /** - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php - */ - public function commandSucceeded(CommandSucceededEvent $event) - { - } - - public function observedNetworkError() : bool - { - return $this->observedNetworkError; - } - - public function start() - { - addSubscriber($this); - } - - public function stop() - { - removeSubscriber($this); - } -} diff --git a/tests/UnifiedSpecTests/EntityMap.php b/tests/UnifiedSpecTests/EntityMap.php index f86715c96..0eaddb53f 100644 --- a/tests/UnifiedSpecTests/EntityMap.php +++ b/tests/UnifiedSpecTests/EntityMap.php @@ -7,15 +7,20 @@ use MongoDB\Client; use MongoDB\Collection; use MongoDB\Database; +use MongoDB\Driver\ClientEncryption; +use MongoDB\Driver\Cursor; use MongoDB\Driver\Session; use MongoDB\GridFS\Bucket; use MongoDB\Tests\UnifiedSpecTests\Constraint\IsBsonType; use PHPUnit\Framework\Assert; use PHPUnit\Framework\Constraint\Constraint; +use ReturnTypeWillChange; use stdClass; + use function array_key_exists; use function PHPUnit\Framework\assertArrayHasKey; use function PHPUnit\Framework\assertArrayNotHasKey; +use function PHPUnit\Framework\assertInstanceOf; use function PHPUnit\Framework\assertIsString; use function PHPUnit\Framework\assertThat; use function PHPUnit\Framework\isInstanceOf; @@ -52,10 +57,8 @@ public function __destruct() } } - /** - * @see http://php.net/arrayaccess.offsetexists - */ - public function offsetExists($id) + /** @see https://php.net/arrayaccess.offsetexists */ + public function offsetExists($id): bool { assertIsString($id); @@ -63,8 +66,10 @@ public function offsetExists($id) } /** - * @see http://php.net/arrayaccess.offsetget + * @see https://php.net/arrayaccess.offsetget + * @return mixed */ + #[ReturnTypeWillChange] public function offsetGet($id) { assertIsString($id); @@ -73,23 +78,19 @@ public function offsetGet($id) return $this->map[$id]->value; } - /** - * @see http://php.net/arrayaccess.offsetset - */ - public function offsetSet($id, $value) + /** @see https://php.net/arrayaccess.offsetset */ + public function offsetSet($id, $value): void { - Assert::fail('Entities can only be set via register()'); + Assert::fail('Entities can only be set via set()'); } - /** - * @see http://php.net/arrayaccess.offsetunset - */ - public function offsetUnset($id) + /** @see https://php.net/arrayaccess.offsetunset */ + public function offsetUnset($id): void { Assert::fail('Entities cannot be removed from the map'); } - public function set(string $id, $value, string $parentId = null) + public function set(string $id, $value, ?string $parentId = null): void { assertArrayNotHasKey($id, $this->map, sprintf('Entity already exists for "%s" and cannot be replaced', $id)); assertThat($value, self::isSupportedType()); @@ -108,14 +109,14 @@ public function set(string $id, $value, string $parentId = null) /** @var self */ public $parent; - public function __construct(string $id, $value, self $parent = null) + public function __construct(string $id, $value, ?self $parent = null) { $this->id = $id; $this->value = $value; $this->parent = $parent; } - public function getRoot() : self + public function getRoot(): self { $root = $this; @@ -128,27 +129,38 @@ public function getRoot() : self }; } - public function getClient(string $clientId) : Client + /** + * Closes a cursor by removing it from the entity map. + * + * @see Operation::executeForCursor() + */ + public function closeCursor(string $cursorId): void + { + assertInstanceOf(Cursor::class, $this[$cursorId]); + unset($this->map[$cursorId]); + } + + public function getClient(string $clientId): Client { return $this[$clientId]; } - public function getCollection(string $collectionId) : Collection + public function getCollection(string $collectionId): Collection { return $this[$collectionId]; } - public function getDatabase(string $databaseId) : Database + public function getDatabase(string $databaseId): Database { return $this[$databaseId]; } - public function getSession(string $sessionId) : Session + public function getSession(string $sessionId): Session { return $this[$sessionId]; } - public function getLogicalSessionId(string $sessionId) : stdClass + public function getLogicalSessionId(string $sessionId): stdClass { return $this->lsidsBySession[$sessionId]; } @@ -160,16 +172,18 @@ public function getRootClientIdOf(string $id) return $root->value instanceof Client ? $root->id : null; } - private static function isSupportedType() : Constraint + private static function isSupportedType(): Constraint { if (self::$isSupportedType === null) { self::$isSupportedType = logicalOr( isInstanceOf(Client::class), + isInstanceOf(ClientEncryption::class), isInstanceOf(Database::class), isInstanceOf(Collection::class), isInstanceOf(Session::class), isInstanceOf(Bucket::class), isInstanceOf(ChangeStream::class), + isInstanceOf(Cursor::class), IsBsonType::any() ); } diff --git a/tests/UnifiedSpecTests/EventCollector.php b/tests/UnifiedSpecTests/EventCollector.php new file mode 100644 index 000000000..8ffcd6287 --- /dev/null +++ b/tests/UnifiedSpecTests/EventCollector.php @@ -0,0 +1,170 @@ + null, + 'PoolReadyEvent' => null, + 'PoolClearedEvent' => null, + 'PoolClosedEvent' => null, + 'ConnectionCreatedEvent' => null, + 'ConnectionReadyEvent' => null, + 'ConnectionClosedEvent' => null, + 'ConnectionCheckOutStartedEvent' => null, + 'ConnectionCheckOutFailedEvent' => null, + 'ConnectionCheckedOutEvent' => null, + 'ConnectionCheckedInEvent' => null, + 'CommandStartedEvent' => CommandStartedEvent::class, + 'CommandSucceededEvent' => CommandSucceededEvent::class, + 'CommandFailedEvent' => CommandFailedEvent::class, + ]; + + /** @var string */ + private $clientId; + + /** @var Context */ + private $context; + + /** @var array */ + private $collectEvents = []; + + /** @var BSONArray */ + private $eventList; + + public function __construct(BSONArray $eventList, array $collectEvents, string $clientId, Context $context) + { + assertNotEmpty($collectEvents); + + foreach ($collectEvents as $event) { + assertIsString($event); + assertArrayHasKey($event, self::$supportedEvents); + + /* CMAP events are "supported" only in the sense that we recognize + * them in the test format; however, PHPC does not implement + * connection pooling so these events cannot be collected. */ + if (self::$supportedEvents[$event] !== null) { + $this->collectEvents[self::$supportedEvents[$event]] = 1; + } + } + + $this->clientId = $clientId; + $this->context = $context; + $this->eventList = $eventList; + } + + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php */ + public function commandFailed(CommandFailedEvent $event): void + { + $this->handleCommandMonitoringEvent($event); + } + + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php */ + public function commandStarted(CommandStartedEvent $event): void + { + $this->handleCommandMonitoringEvent($event); + } + + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php */ + public function commandSucceeded(CommandSucceededEvent $event): void + { + $this->handleCommandMonitoringEvent($event); + } + + public function start(): void + { + addSubscriber($this); + } + + public function stop(): void + { + removeSubscriber($this); + } + + /** @param CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event */ + private function handleCommandMonitoringEvent($event): void + { + assertIsObject($event); + + if (! $this->context->isActiveClient($this->clientId)) { + return; + } + + if (! isset($this->collectEvents[get_class($event)])) { + return; + } + + $log = [ + 'name' => self::getEventName($event), + 'observedAt' => microtime(true), + 'commandName' => $event->getCommandName(), + 'connectionId' => self::getConnectionId($event), + 'requestId' => $event->getRequestId(), + 'operationId' => $event->getOperationId(), + ]; + + /* Note: CommandStartedEvent.command and CommandSucceededEvent.reply can + * be omitted from logged events. */ + + if ($event instanceof CommandStartedEvent) { + $log['databaseName'] = $event->getDatabaseName(); + } + + if ($event instanceof CommandSucceededEvent) { + $log['duration'] = $event->getDurationMicros(); + } + + if ($event instanceof CommandFailedEvent) { + $log['failure'] = $event->getError()->getMessage(); + $log['duration'] = $event->getDurationMicros(); + } + + $this->eventList[] = $log; + } + + /** @param CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event */ + private static function getConnectionId($event): string + { + $server = $event->getServer(); + + return sprintf('%s:%d', $server->getHost(), $server->getPort()); + } + + private static function getEventName(object $event): string + { + static $eventNamesByClass = null; + + if ($eventNamesByClass === null) { + $eventNamesByClass = array_flip(array_filter(self::$supportedEvents)); + } + + return $eventNamesByClass[get_class($event)]; + } +} diff --git a/tests/UnifiedSpecTests/EventObserver.php b/tests/UnifiedSpecTests/EventObserver.php index b4c58d307..6995dd995 100644 --- a/tests/UnifiedSpecTests/EventObserver.php +++ b/tests/UnifiedSpecTests/EventObserver.php @@ -11,7 +11,7 @@ use MultipleIterator; use PHPUnit\Framework\Assert; use stdClass; -use function array_fill_keys; + use function array_reverse; use function count; use function current; @@ -22,7 +22,9 @@ use function MongoDB\Driver\Monitoring\removeSubscriber; use function PHPUnit\Framework\assertArrayHasKey; use function PHPUnit\Framework\assertCount; +use function PHPUnit\Framework\assertGreaterThanOrEqual; use function PHPUnit\Framework\assertInstanceOf; +use function PHPUnit\Framework\assertIsBool; use function PHPUnit\Framework\assertIsObject; use function PHPUnit\Framework\assertIsString; use function PHPUnit\Framework\assertNotEmpty; @@ -31,23 +33,42 @@ use function PHPUnit\Framework\assertThat; use function sprintf; -class EventObserver implements CommandSubscriber +/** + * EventObserver handles "observeEvents" for client entities and assertions for + * "expectEvents" and special operations (e.g. assertSameLsidOnLastTwoCommands). + */ +final class EventObserver implements CommandSubscriber { - /** @var array */ - private static $defaultIgnoreCommands = [ - // failPoint and targetedFailPoint operations - 'configureFailPoint', - // See: https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst#security - 'authenticate', - 'saslStart', - 'saslContinue', - 'getnonce', - 'createUser', - 'updateUser', - 'copydbgetnonce', - 'copydbsaslstart', - 'copydb', - 'isMaster', + /** + * These commands are always considered sensitive (i.e. command and reply + * documents should be redacted). + * + * @see https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst#security + * @var array + */ + private static $sensitiveCommands = [ + 'authenticate' => 1, + 'saslStart' => 1, + 'saslContinue' => 1, + 'getnonce' => 1, + 'createUser' => 1, + 'updateUser' => 1, + 'copydbgetnonce' => 1, + 'copydbsaslstart' => 1, + 'copydb' => 1, + ]; + + /** + * These commands are only considered sensitive when the command or reply + * document includes a speculativeAuthenticate field. + * + * @see https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst#security + * @var array + */ + private static $sensitiveCommandsWithSpeculativeAuthenticate = [ + 'ismaster' => 1, + 'isMaster' => 1, + 'hello' => 1, ]; /** @var array */ @@ -57,6 +78,26 @@ class EventObserver implements CommandSubscriber 'commandFailedEvent' => CommandFailedEvent::class, ]; + /** + * These events are defined in the specification but unsupported by PHPLIB + * (e.g. CMAP events). + * + * @var array + */ + private static $unsupportedEvents = [ + 'poolCreatedEvent' => 1, + 'poolReadyEvent' => 1, + 'poolClearedEvent' => 1, + 'poolClosedEvent' => 1, + 'connectionCreatedEvent' => 1, + 'connectionReadyEvent' => 1, + 'connectionClosedEvent' => 1, + 'connectionCheckOutStartedEvent' => 1, + 'connectionCheckOutFailedEvent' => 1, + 'connectionCheckedOutEvent' => 1, + 'connectionCheckedInEvent' => 1, + ]; + /** @var array */ private $actualEvents = []; @@ -66,68 +107,78 @@ class EventObserver implements CommandSubscriber /** @var Context */ private $context; - /** @var array */ - private $ignoreCommands = []; + /** + * The configureFailPoint command (used by failPoint and targetedFailPoint + * operations) is always ignored. + * + * @var array + */ + private $ignoreCommands = ['configureFailPoint' => 1]; /** @var array */ private $observeEvents = []; - public function __construct(array $observeEvents, array $ignoreCommands, string $clientId, Context $context) + /** @var bool */ + private $observeSensitiveCommands; + + public function __construct(array $observeEvents, array $ignoreCommands, bool $observeSensitiveCommands, string $clientId, Context $context) { assertNotEmpty($observeEvents); foreach ($observeEvents as $event) { assertIsString($event); + + /* Unlike Context::assertExpectedEventsForClients, which runs within + * a test, EventObserver is constructed via createEntities (before + * all tests). Ignoring events here allows tests within the file + * that don't assert these events to still execute. */ + if (isset(self::$unsupportedEvents[$event])) { + continue; + } + assertArrayHasKey($event, self::$supportedEvents); $this->observeEvents[self::$supportedEvents[$event]] = 1; } - $this->ignoreCommands = array_fill_keys(self::$defaultIgnoreCommands, 1); - foreach ($ignoreCommands as $command) { assertIsString($command); $this->ignoreCommands[$command] = 1; } + $this->observeSensitiveCommands = $observeSensitiveCommands; $this->clientId = $clientId; $this->context = $context; } - /** - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php - */ - public function commandFailed(CommandFailedEvent $event) + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php */ + public function commandFailed(CommandFailedEvent $event): void { $this->handleEvent($event); } - /** - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php - */ - public function commandStarted(CommandStartedEvent $event) + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php */ + public function commandStarted(CommandStartedEvent $event): void { $this->handleEvent($event); } - /** - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php - */ - public function commandSucceeded(CommandSucceededEvent $event) + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php */ + public function commandSucceeded(CommandSucceededEvent $event): void { $this->handleEvent($event); } - public function start() + public function start(): void { addSubscriber($this); } - public function stop() + public function stop(): void { removeSubscriber($this); } - public function getLsidsOnLastTwoCommands() : array + public function getLsidsOnLastTwoCommands(): array { $lsids = []; @@ -148,16 +199,24 @@ public function getLsidsOnLastTwoCommands() : array Assert::fail('Not enough CommandStartedEvents observed'); } - public function assert(array $expectedEvents) + public function assert(array $expectedEvents, bool $ignoreExtraEvents): void { - assertCount(count($expectedEvents), $this->actualEvents); + if ($ignoreExtraEvents) { + assertGreaterThanOrEqual(count($expectedEvents), count($this->actualEvents)); + } else { + assertCount(count($expectedEvents), $this->actualEvents); + } $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); $mi->attachIterator(new ArrayIterator($expectedEvents)); $mi->attachIterator(new ArrayIterator($this->actualEvents)); foreach ($mi as $keys => $events) { - list($expectedEvent, $actualEvent) = $events; + [$expectedEvent, $actualEvent] = $events; + + if ($ignoreExtraEvents && $expectedEvent === null) { + break; + } assertIsObject($expectedEvent); $expectedEvent = (array) $expectedEvent; @@ -183,18 +242,21 @@ private function assertEvent($actual, stdClass $expected, string $message) switch (get_class($actual)) { case CommandStartedEvent::class: return $this->assertCommandStartedEvent($actual, $expected, $message); + case CommandSucceededEvent::class: return $this->assertCommandSucceededEvent($actual, $expected, $message); + case CommandFailedEvent::class: return $this->assertCommandFailedEvent($actual, $expected, $message); + default: Assert::fail($message . ': Unsupported event type: ' . get_class($actual)); } } - private function assertCommandStartedEvent(CommandStartedEvent $actual, stdClass $expected, string $message) + private function assertCommandStartedEvent(CommandStartedEvent $actual, stdClass $expected, string $message): void { - Util::assertHasOnlyKeys($expected, ['command', 'commandName', 'databaseName']); + Util::assertHasOnlyKeys($expected, ['command', 'commandName', 'databaseName', 'hasServiceId', 'hasServerConnectionId']); if (isset($expected->command)) { assertIsObject($expected->command); @@ -211,11 +273,21 @@ private function assertCommandStartedEvent(CommandStartedEvent $actual, stdClass assertIsString($expected->databaseName); assertSame($actual->getDatabaseName(), $expected->databaseName, $message . ': databaseName matches'); } + + if (isset($expected->hasServiceId)) { + assertIsBool($expected->hasServiceId); + assertSame($actual->getServiceId() !== null, $expected->hasServiceId, $message . ': hasServiceId matches'); + } + + if (isset($expected->hasServerConnectionId)) { + assertIsBool($expected->hasServerConnectionId); + assertSame($actual->getServerConnectionId() !== null, $expected->hasServerConnectionId, $message . ': hasServerConnectionId matches'); + } } - private function assertCommandSucceededEvent(CommandSucceededEvent $actual, stdClass $expected, string $message) + private function assertCommandSucceededEvent(CommandSucceededEvent $actual, stdClass $expected, string $message): void { - Util::assertHasOnlyKeys($expected, ['reply', 'commandName']); + Util::assertHasOnlyKeys($expected, ['reply', 'commandName', 'hasServiceId', 'hasServerConnectionId']); if (isset($expected->reply)) { assertIsObject($expected->reply); @@ -227,20 +299,40 @@ private function assertCommandSucceededEvent(CommandSucceededEvent $actual, stdC assertIsString($expected->commandName); assertSame($actual->getCommandName(), $expected->commandName, $message . ': commandName matches'); } + + if (isset($expected->hasServiceId)) { + assertIsBool($expected->hasServiceId); + assertSame($actual->getServiceId() !== null, $expected->hasServiceId, $message . ': hasServiceId matches'); + } + + if (isset($expected->hasServerConnectionId)) { + assertIsBool($expected->hasServerConnectionId); + assertSame($actual->getServerConnectionId() !== null, $expected->hasServerConnectionId, $message . ': hasServerConnectionId matches'); + } } - private function assertCommandFailedEvent(CommandFailedEvent $actual, stdClass $expected, string $message) + private function assertCommandFailedEvent(CommandFailedEvent $actual, stdClass $expected, string $message): void { - Util::assertHasOnlyKeys($expected, ['commandName']); + Util::assertHasOnlyKeys($expected, ['commandName', 'hasServiceId', 'hasServerConnectionId']); if (isset($expected->commandName)) { assertIsString($expected->commandName); assertSame($actual->getCommandName(), $expected->commandName, $message . ': commandName matches'); } + + if (isset($expected->hasServiceId)) { + assertIsBool($expected->hasServiceId); + assertSame($actual->getServiceId() !== null, $expected->hasServiceId, $message . ': hasServiceId matches'); + } + + if (isset($expected->hasServerConnectionId)) { + assertIsBool($expected->hasServerConnectionId); + assertSame($actual->getServerConnectionId() !== null, $expected->hasServerConnectionId, $message . ': hasServerConnectionId matches'); + } } /** @param CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event */ - private function handleEvent($event) + private function handleEvent($event): void { if (! $this->context->isActiveClient($this->clientId)) { return; @@ -258,6 +350,30 @@ private function handleEvent($event) return; } + if (! $this->observeSensitiveCommands && $this->isSensitiveCommand($event)) { + return; + } + $this->actualEvents[] = $event; } + + /** @param CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event */ + private function isSensitiveCommand($event): bool + { + if (isset(self::$sensitiveCommands[$event->getCommandName()])) { + return true; + } + + /* If the command or reply included a speculativeAuthenticate field, + * libmongoc will already have redacted it (CDRIVER-4000). Therefore, we + * can infer that the command was sensitive if its command or reply is + * empty. */ + if (isset(self::$sensitiveCommandsWithSpeculativeAuthenticate[$event->getCommandName()])) { + $commandOrReply = $event instanceof CommandStartedEvent ? $event->getCommand() : $event->getReply(); + + return (array) $commandOrReply === []; + } + + return false; + } } diff --git a/tests/UnifiedSpecTests/ExpectedError.php b/tests/UnifiedSpecTests/ExpectedError.php index d5c5c322e..a57ef341a 100644 --- a/tests/UnifiedSpecTests/ExpectedError.php +++ b/tests/UnifiedSpecTests/ExpectedError.php @@ -7,24 +7,33 @@ use MongoDB\Driver\Exception\ExecutionTimeoutException; use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Exception\ServerException; +use MongoDB\Driver\Exception\WriteException; +use MongoDB\Tests\UnifiedSpecTests\Constraint\Matches; use PHPUnit\Framework\Assert; use stdClass; use Throwable; + use function get_class; use function PHPUnit\Framework\assertArrayHasKey; use function PHPUnit\Framework\assertContainsOnly; +use function PHPUnit\Framework\assertCount; use function PHPUnit\Framework\assertFalse; use function PHPUnit\Framework\assertInstanceOf; use function PHPUnit\Framework\assertIsArray; use function PHPUnit\Framework\assertIsBool; use function PHPUnit\Framework\assertIsInt; +use function PHPUnit\Framework\assertIsObject; use function PHPUnit\Framework\assertIsString; +use function PHPUnit\Framework\assertNotInstanceOf; use function PHPUnit\Framework\assertNotNull; use function PHPUnit\Framework\assertNull; use function PHPUnit\Framework\assertObjectHasAttribute; use function PHPUnit\Framework\assertSame; use function PHPUnit\Framework\assertStringContainsStringIgnoringCase; +use function PHPUnit\Framework\assertThat; use function PHPUnit\Framework\assertTrue; +use function PHPUnit\Framework\isInstanceOf; +use function PHPUnit\Framework\logicalOr; use function property_exists; use function sprintf; @@ -57,6 +66,9 @@ final class ExpectedError /** @var string|null */ private $codeName; + /** @var Matches|null */ + private $matchesResultDocument; + /** @var array */ private $includedLabels = []; @@ -66,7 +78,7 @@ final class ExpectedError /** @var ExpectedResult|null */ private $expectedResult; - public function __construct(stdClass $o = null, EntityMap $entityMap) + public function __construct(?stdClass $o, EntityMap $entityMap) { if ($o === null) { return; @@ -98,6 +110,11 @@ public function __construct(stdClass $o = null, EntityMap $entityMap) $this->codeName = $o->errorCodeName; } + if (isset($o->errorResponse)) { + assertIsObject($o->errorResponse); + $this->matchesResultDocument = new Matches($o->errorResponse, $entityMap); + } + if (isset($o->errorLabelsContain)) { assertIsArray($o->errorLabelsContain); assertContainsOnly('string', $o->errorLabelsContain); @@ -120,7 +137,7 @@ public function __construct(stdClass $o = null, EntityMap $entityMap) * * @param Throwable|null $e Exception (if any) from executing an operation */ - public function assert(Throwable $e = null) + public function assert(?Throwable $e = null): void { if (! $this->isError && $e !== null) { Assert::fail(sprintf("Operation threw unexpected %s: %s\n%s", get_class($e), $e->getMessage(), $e->getTraceAsString())); @@ -134,6 +151,10 @@ public function assert(Throwable $e = null) assertNotNull($e); + if (isset($this->isClientError)) { + $this->assertIsClientError($e); + } + if (isset($this->messageContains)) { assertStringContainsStringIgnoringCase($this->messageContains, $e->getMessage()); } @@ -148,6 +169,18 @@ public function assert(Throwable $e = null) $this->assertCodeName($e); } + if (isset($this->matchesResultDocument)) { + assertThat($e, logicalOr(isInstanceOf(CommandException::class), isInstanceOf(WriteException::class))); + + if ($e instanceof CommandException) { + assertThat($e->getResultDocument(), $this->matchesResultDocument, 'CommandException result document matches'); + } elseif ($e instanceof WriteException) { + $writeErrors = $e->getWriteResult()->getErrorReplies(); + assertCount(1, $writeErrors); + assertThat($writeErrors[0], $this->matchesResultDocument, 'WriteException result document matches'); + } + } + if (! empty($this->excludedLabels) || ! empty($this->includedLabels)) { assertInstanceOf(RuntimeException::class, $e); @@ -166,7 +199,22 @@ public function assert(Throwable $e = null) } } - private function assertCodeName(ServerException $e) + private function assertIsClientError(Throwable $e): void + { + /* Note: BulkWriteException may proxy a previous exception. Unwrap it + * to check the original error. */ + if ($e instanceof BulkWriteException && $e->getPrevious() !== null) { + $e = $e->getPrevious(); + } + + if ($this->isClientError) { + assertNotInstanceOf(ServerException::class, $e); + } else { + assertInstanceOf(ServerException::class, $e); + } + } + + private function assertCodeName(ServerException $e): void { /* BulkWriteException and ExecutionTimeoutException do not expose * codeName. Work around this by translating it to a numeric code. diff --git a/tests/UnifiedSpecTests/ExpectedResult.php b/tests/UnifiedSpecTests/ExpectedResult.php index f51da2e1c..618a2983d 100644 --- a/tests/UnifiedSpecTests/ExpectedResult.php +++ b/tests/UnifiedSpecTests/ExpectedResult.php @@ -10,6 +10,7 @@ use MongoDB\Tests\UnifiedSpecTests\Constraint\Matches; use MongoDB\UpdateResult; use stdClass; + use function is_object; use function PHPUnit\Framework\assertThat; use function property_exists; @@ -30,7 +31,7 @@ final class ExpectedResult */ private $yieldingEntityId; - public function __construct(stdClass $o, EntityMap $entityMap, string $yieldingEntityId = null) + public function __construct(stdClass $o, EntityMap $entityMap, ?string $yieldingEntityId = null) { if (property_exists($o, 'expectResult')) { $this->constraint = new Matches($o->expectResult, $entityMap); @@ -40,7 +41,7 @@ public function __construct(stdClass $o, EntityMap $entityMap, string $yieldingE $this->yieldingEntityId = $yieldingEntityId; } - public function assert($actual, string $saveResultAsEntity = null) + public function assert($actual, ?string $saveResultAsEntity = null): void { if ($this->constraint === null && $saveResultAsEntity === null) { return; @@ -63,12 +64,14 @@ private static function prepare($value) return $value; } - if ($value instanceof BulkWriteResult || + if ( + $value instanceof BulkWriteResult || $value instanceof WriteResult || $value instanceof DeleteResult || $value instanceof InsertOneResult || $value instanceof InsertManyResult || - $value instanceof UpdateResult) { + $value instanceof UpdateResult + ) { return self::prepareWriteResult($value); } diff --git a/tests/UnifiedSpecTests/FailPointObserver.php b/tests/UnifiedSpecTests/FailPointObserver.php index f7794f7fc..7f8c3a9e6 100644 --- a/tests/UnifiedSpecTests/FailPointObserver.php +++ b/tests/UnifiedSpecTests/FailPointObserver.php @@ -7,6 +7,7 @@ use MongoDB\Driver\Monitoring\CommandSubscriber; use MongoDB\Driver\Monitoring\CommandSucceededEvent; use MongoDB\Operation\DatabaseCommand; + use function MongoDB\Driver\Monitoring\addSubscriber; use function MongoDB\Driver\Monitoring\removeSubscriber; @@ -15,17 +16,13 @@ class FailPointObserver implements CommandSubscriber /** @var array */ private $failPointsAndServers = []; - /** - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php - */ - public function commandFailed(CommandFailedEvent $event) + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php */ + public function commandFailed(CommandFailedEvent $event): void { } - /** - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php - */ - public function commandStarted(CommandStartedEvent $event) + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php */ + public function commandStarted(CommandStartedEvent $event): void { $command = $event->getCommand(); @@ -40,16 +37,14 @@ public function commandStarted(CommandStartedEvent $event) $this->failPointsAndServers[] = [$command->configureFailPoint, $event->getServer()]; } - /** - * @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php - */ - public function commandSucceeded(CommandSucceededEvent $event) + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php */ + public function commandSucceeded(CommandSucceededEvent $event): void { } - public function disableFailPoints() + public function disableFailPoints(): void { - foreach ($this->failPointsAndServers as list($failPoint, $server)) { + foreach ($this->failPointsAndServers as [$failPoint, $server]) { $operation = new DatabaseCommand('admin', ['configureFailPoint' => $failPoint, 'mode' => 'off']); $operation->execute($server); } @@ -57,12 +52,12 @@ public function disableFailPoints() $this->failPointsAndServers = []; } - public function start() + public function start(): void { addSubscriber($this); } - public function stop() + public function stop(): void { removeSubscriber($this); } diff --git a/tests/UnifiedSpecTests/Loop.php b/tests/UnifiedSpecTests/Loop.php new file mode 100644 index 000000000..3351e22b8 --- /dev/null +++ b/tests/UnifiedSpecTests/Loop.php @@ -0,0 +1,181 @@ +operations = $operations; + $this->context = $context; + + foreach (['storeErrorsAsEntity', 'storeFailuresAsEntity', 'storeSuccessesAsEntity', 'storeIterationsAsEntity'] as $option) { + if (array_key_exists($option, $options)) { + assertIsString($options[$option]); + } + } + + $errorListEntityId = $options['storeErrorsAsEntity'] ?? ($options['storeFailuresAsEntity'] ?? null); + $failureListEntityId = $options['storeFailuresAsEntity'] ?? ($options['storeErrorsAsEntity'] ?? null); + + if (isset($errorListEntityId)) { + $this->errorList = $this->initializeListEntity($errorListEntityId); + } + + if (isset($failureListEntityId)) { + $this->failureList = $this->initializeListEntity($failureListEntityId); + } + + $this->numSuccessfulOperationsEntityId = $options['storeSuccessesAsEntity'] ?? null; + $this->numIterationsEntityId = $options['storeIterationsAsEntity'] ?? null; + } + + public function execute(): void + { + assertFalse($this->context->isInLoop(), 'Nested loops are unsupported'); + + $numIterations = 0; + $numSuccessfulOperations = 0; + + $callback = function () use (&$numSuccessfulOperations): void { + foreach ($this->operations as $operation) { + $operation->assert(); + $numSuccessfulOperations++; + } + }; + + $this->context->setInLoop(true); + + try { + while (self::$allowIteration) { + $numIterations++; + try { + call_user_func($callback); + } catch (Throwable $e) { + /* Allow internal PHP errors and certain PHPUnit exceptions + * to interrupt the loop, as they are not expected here. */ + if ($e instanceof Error || $e instanceof IncompleteTest || $e instanceof SkippedTest || $e instanceof Warning) { + throw $e; + } + + $this->handleErrorOrFailure($e); + } + + if (self::$sleepUsecBetweenIterations > 0) { + usleep(self::$sleepUsecBetweenIterations); + } + } + } finally { + $this->context->setInLoop(false); + + $entityMap = $this->context->getEntityMap(); + + if (isset($this->numSuccessfulOperationsEntityId)) { + $entityMap->set($this->numSuccessfulOperationsEntityId, $numSuccessfulOperations); + } + + if (isset($this->numIterationsEntityId)) { + $entityMap->set($this->numIterationsEntityId, $numIterations); + } + } + } + + /** + * Allow or prohibit loop operations from starting a new iteration. + * + * This function is primarily used by the Atlas testing workload executor. + */ + public static function allowIteration(bool $allowIteration = true): void + { + self::$allowIteration = $allowIteration; + } + + /** + * Set time to sleep between iterations. + * + * This can be used to limit CPU usage during workload execution. + */ + public static function setSleepUsecBetweenIterations(int $usec): void + { + assertGreaterThanOrEqual(0, $usec); + + self::$sleepUsecBetweenIterations = $usec; + } + + private function handleErrorOrFailure(Throwable $e): void + { + /* The constructor will either initialize both lists or leave them both + * unset. If unset, exceptions should not be logged and instead + * interrupt the loop. */ + if (! isset($this->errorList, $this->failureList)) { + throw $e; + } + + /* Failures and errors are differentiated according to the logic in + * PHPUnit\Framework\TestCase::runBare(). Other PHPUnit exceptions have + * already been excluded by logic in execute(). */ + $list = $e instanceof AssertionFailedError ? $this->failureList : $this->errorList; + + $list->append([ + 'error' => $e->getMessage(), + 'time' => microtime(true), + ]); + } + + private function initializeListEntity(string $id): BSONArray + { + $entityMap = $this->context->getEntityMap(); + + if (! $entityMap->offsetExists($id)) { + $entityMap->set($id, new BSONArray()); + } + + assertInstanceOf(BSONArray::class, $entityMap[$id]); + + return $entityMap[$id]; + } +} diff --git a/tests/UnifiedSpecTests/Operation.php b/tests/UnifiedSpecTests/Operation.php index be9b86664..d2793a239 100644 --- a/tests/UnifiedSpecTests/Operation.php +++ b/tests/UnifiedSpecTests/Operation.php @@ -2,23 +2,31 @@ namespace MongoDB\Tests\UnifiedSpecTests; +use Error; +use MongoDB\BSON\Javascript; use MongoDB\ChangeStream; use MongoDB\Client; use MongoDB\Collection; use MongoDB\Database; +use MongoDB\Driver\ClientEncryption; +use MongoDB\Driver\Cursor; use MongoDB\Driver\Server; use MongoDB\Driver\Session; use MongoDB\GridFS\Bucket; +use MongoDB\Model\CollectionInfo; +use MongoDB\Model\DatabaseInfo; use MongoDB\Model\IndexInfo; use MongoDB\Operation\DatabaseCommand; use MongoDB\Operation\FindOneAndReplace; use MongoDB\Operation\FindOneAndUpdate; use PHPUnit\Framework\Assert; -use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Constraint\IsType; +use PHPUnit\Framework\Exception as PHPUnitException; use stdClass; use Throwable; -use Traversable; + use function array_diff_key; +use function array_intersect_key; use function array_key_exists; use function array_map; use function current; @@ -29,12 +37,14 @@ use function iterator_to_array; use function key; use function MongoDB\with_transaction; +use function PHPUnit\Framework\assertArrayHasKey; use function PHPUnit\Framework\assertContains; use function PHPUnit\Framework\assertCount; use function PHPUnit\Framework\assertEquals; use function PHPUnit\Framework\assertFalse; use function PHPUnit\Framework\assertInstanceOf; use function PHPUnit\Framework\assertIsArray; +use function PHPUnit\Framework\assertIsInt; use function PHPUnit\Framework\assertIsObject; use function PHPUnit\Framework\assertIsString; use function PHPUnit\Framework\assertMatchesRegularExpression; @@ -55,7 +65,7 @@ final class Operation { - const OBJECT_TEST_RUNNER = 'testRunner'; + public const OBJECT_TEST_RUNNER = 'testRunner'; /** @var bool */ private $isTestRunnerOperation; @@ -76,17 +86,20 @@ final class Operation private $entityMap; /** @var ExpectedError */ - private $expectedError; + private $expectError; /** @var ExpectedResult */ - private $expectedResult; + private $expectResult; + + /** @var bool */ + private $ignoreResultAndError; /** @var string */ private $saveResultAsEntity; public function __construct(stdClass $o, Context $context) { - $this->context =$context; + $this->context = $context; $this->entityMap = $context->getEntityMap(); assertIsString($o->name); @@ -101,10 +114,15 @@ public function __construct(stdClass $o, Context $context) $this->arguments = (array) $o->arguments; } + if (isset($o->ignoreResultAndError) && (isset($o->expectError) || property_exists($o, 'expectResult') || isset($o->saveResultAsEntity))) { + Assert::fail('ignoreResultAndError is mutually exclusive with expectError, expectResult, and saveResultAsEntity'); + } + if (isset($o->expectError) && (property_exists($o, 'expectResult') || isset($o->saveResultAsEntity))) { Assert::fail('expectError is mutually exclusive with expectResult and saveResultAsEntity'); } + $this->ignoreResultAndError = $o->ignoreResultAndError ?? false; $this->expectError = new ExpectedError($o->expectError ?? null, $this->entityMap); $this->expectResult = new ExpectedResult($o, $this->entityMap, $this->object); @@ -117,47 +135,35 @@ public function __construct(stdClass $o, Context $context) /** * Execute the operation and assert its outcome. */ - public function assert(bool $rethrowExceptions = false) + public function assert(bool $rethrowExceptions = false): void { $error = null; $result = null; $saveResultAsEntity = null; - if (isset($this->arguments['session'])) { - $dirtySessionObserver = new DirtySessionObserver($this->entityMap->getLogicalSessionId($this->arguments['session'])); - $dirtySessionObserver->start(); - } - try { $result = $this->execute(); $saveResultAsEntity = $this->saveResultAsEntity; } catch (Throwable $e) { - /* Note: we must be selective about what PHPUnit exceptions to pass - * through, as PHPUnit's Warning exception must be considered for - * expectError in GridFS tests (see: PHPLIB-592). + /* Rethrow any internal PHP errors and PHPUnit exceptions, since + * those are never expected for "expectError". * * TODO: Consider adding operation details (e.g. operations[] index) * to the exception message. Alternatively, throw a new exception * and include this as the previous, since PHPUnit will render the * chain when reporting a test failure. */ - if ($e instanceof AssertionFailedError) { + if ($e instanceof Error || $e instanceof PHPUnitException) { throw $e; } $error = $e; } - if (isset($dirtySessionObserver)) { - $dirtySessionObserver->stop(); - - if ($dirtySessionObserver->observedNetworkError()) { - $this->context->markDirtySession($this->arguments['session']); - } + if (! $this->ignoreResultAndError) { + $this->expectError->assert($error); + $this->expectResult->assert($result, $saveResultAsEntity); } - $this->expectError->assert($error); - $this->expectResult->assert($result, $saveResultAsEntity); - // Rethrowing is primarily used for withTransaction callbacks if ($error && $rethrowExceptions) { throw $error; @@ -181,6 +187,9 @@ private function execute() case Client::class: $result = $this->executeForClient($object); break; + case ClientEncryption::class: + $result = $this->executeForClientEncryption($object); + break; case Database::class: $result = $this->executeForDatabase($object); break; @@ -190,6 +199,9 @@ private function execute() case ChangeStream::class: $result = $this->executeForChangeStream($object); break; + case Cursor::class: + $result = $this->executeForCursor($object); + break; case Session::class: $result = $this->executeForSession($object); break; @@ -200,16 +212,13 @@ private function execute() Assert::fail('Unsupported entity type: ' . get_class($object)); } - if ($result instanceof Traversable && ! $result instanceof ChangeStream) { - return iterator_to_array($result); - } - return $result; } private function executeForChangeStream(ChangeStream $changeStream) { $args = $this->prepareArguments(); + Util::assertArgumentsBySchema(ChangeStream::class, $this->name, $args); switch ($this->name) { case 'iterateUntilDocumentOrError': @@ -233,97 +242,225 @@ private function executeForChangeStream(ChangeStream $changeStream) } while (! $changeStream->valid()); return $changeStream->current(); + default: - Assert::fail('Unsupported client operation: ' . $this->name); + Assert::fail('Unsupported change stream operation: ' . $this->name); } } private function executeForClient(Client $client) { $args = $this->prepareArguments(); + Util::assertArgumentsBySchema(Client::class, $this->name, $args); switch ($this->name) { case 'createChangeStream': - $changeStream = $client->watch( - $args['pipeline'] ?? [], + assertArrayHasKey('pipeline', $args); + assertIsArray($args['pipeline']); + + return $client->watch( + $args['pipeline'], array_diff_key($args, ['pipeline' => 1]) ); - $changeStream->rewind(); - return $changeStream; case 'listDatabaseNames': - return $client->listDatabaseNames($args); + return iterator_to_array($client->listDatabaseNames($args)); + case 'listDatabases': - return $client->listDatabases($args); + return array_map( + function (DatabaseInfo $info) { + return $info->__debugInfo(); + }, + iterator_to_array($client->listDatabases($args)) + ); + default: Assert::fail('Unsupported client operation: ' . $this->name); } } + private function executeForClientEncryption(ClientEncryption $clientEncryption) + { + $args = $this->prepareArguments(); + Util::assertArgumentsBySchema(ClientEncryption::class, $this->name, $args); + + switch ($this->name) { + case 'addKeyAltName': + assertArrayHasKey('id', $args); + assertArrayHasKey('keyAltName', $args); + + return $clientEncryption->addKeyAltName($args['id'], $args['keyAltName']); + + case 'createDataKey': + assertArrayHasKey('kmsProvider', $args); + // CSFLE spec tests nest options under an "opts" key (see: DRIVERS-2414) + $options = array_key_exists('opts', $args) ? (array) $args['opts'] : []; + + return $clientEncryption->createDataKey($args['kmsProvider'], $options); + + case 'deleteKey': + assertArrayHasKey('id', $args); + + return $clientEncryption->deleteKey($args['id']); + + case 'getKey': + assertArrayHasKey('id', $args); + + return $clientEncryption->getKey($args['id']); + + case 'getKeyByAltName': + assertArrayHasKey('keyAltName', $args); + + return $clientEncryption->getKeyByAltName($args['keyAltName']); + + case 'getKeys': + return iterator_to_array($clientEncryption->getKeys()); + + case 'removeKeyAltName': + assertArrayHasKey('id', $args); + assertArrayHasKey('keyAltName', $args); + + return $clientEncryption->removeKeyAltName($args['id'], $args['keyAltName']); + + case 'rewrapManyDataKey': + assertArrayHasKey('filter', $args); + // CSFLE spec tests nest options under an "opts" key (see: DRIVERS-2414) + $options = array_key_exists('opts', $args) ? (array) $args['opts'] : []; + + return static::prepareRewrapManyDataKeyResult($clientEncryption->rewrapManyDataKey($args['filter'], $options)); + + default: + Assert::fail('Unsupported clientEncryption operation: ' . $this->name); + } + } + private function executeForCollection(Collection $collection) { $args = $this->prepareArguments(); + Util::assertArgumentsBySchema(Collection::class, $this->name, $args); switch ($this->name) { case 'aggregate': - return $collection->aggregate( + assertArrayHasKey('pipeline', $args); + assertIsArray($args['pipeline']); + + return iterator_to_array($collection->aggregate( $args['pipeline'], array_diff_key($args, ['pipeline' => 1]) - ); + )); + case 'bulkWrite': + assertArrayHasKey('requests', $args); + assertIsArray($args['requests']); + return $collection->bulkWrite( - array_map('self::prepareBulkWriteRequest', $args['requests']), + array_map( + static function ($request) { + return self::prepareBulkWriteRequest($request); + }, + $args['requests'] + ), array_diff_key($args, ['requests' => 1]) ); + case 'createChangeStream': - $changeStream = $collection->watch( - $args['pipeline'] ?? [], + assertArrayHasKey('pipeline', $args); + assertIsArray($args['pipeline']); + + return $collection->watch( + $args['pipeline'], array_diff_key($args, ['pipeline' => 1]) ); - $changeStream->rewind(); - return $changeStream; + case 'createFindCursor': + assertArrayHasKey('filter', $args); + assertInstanceOf(stdClass::class, $args['filter']); + + return $collection->find( + $args['filter'], + array_diff_key($args, ['filter' => 1]) + ); + case 'createIndex': + assertArrayHasKey('keys', $args); + assertInstanceOf(stdClass::class, $args['keys']); + return $collection->createIndex( $args['keys'], array_diff_key($args, ['keys' => 1]) ); + case 'dropIndex': + assertArrayHasKey('name', $args); + assertIsString($args['name']); + return $collection->dropIndex( $args['name'], array_diff_key($args, ['name' => 1]) ); + case 'count': case 'countDocuments': - case 'find': + assertArrayHasKey('filter', $args); + assertInstanceOf(stdClass::class, $args['filter']); + return $collection->{$this->name}( - $args['filter'] ?? [], + $args['filter'], array_diff_key($args, ['filter' => 1]) ); + case 'estimatedDocumentCount': return $collection->estimatedDocumentCount($args); + case 'deleteMany': case 'deleteOne': case 'findOneAndDelete': + assertArrayHasKey('filter', $args); + assertInstanceOf(stdClass::class, $args['filter']); + return $collection->{$this->name}( $args['filter'], array_diff_key($args, ['filter' => 1]) ); + case 'distinct': if (isset($args['session']) && $args['session']->isInTransaction()) { // Transaction, but sharded cluster? $collection->distinct('foo'); } + assertArrayHasKey('fieldName', $args); + assertArrayHasKey('filter', $args); + assertIsString($args['fieldName']); + assertInstanceOf(stdClass::class, $args['filter']); + return $collection->distinct( $args['fieldName'], - $args['filter'] ?? [], + $args['filter'], array_diff_key($args, ['fieldName' => 1, 'filter' => 1]) ); + case 'drop': return $collection->drop($args); + + case 'find': + assertArrayHasKey('filter', $args); + assertInstanceOf(stdClass::class, $args['filter']); + + return iterator_to_array($collection->find( + $args['filter'], + array_diff_key($args, ['filter' => 1]) + )); + case 'findOne': - return $collection->findOne($args['filter'], array_diff_key($args, ['filter' => 1])); + assertArrayHasKey('filter', $args); + assertInstanceOf(stdClass::class, $args['filter']); + + return $collection->findOne( + $args['filter'], + array_diff_key($args, ['filter' => 1]) + ); + case 'findOneAndReplace': if (isset($args['returnDocument'])) { $args['returnDocument'] = strtolower($args['returnDocument']); @@ -334,13 +471,18 @@ private function executeForCollection(Collection $collection) : FindOneAndReplace::RETURN_DOCUMENT_BEFORE; } // Fall through - case 'replaceOne': + assertArrayHasKey('filter', $args); + assertArrayHasKey('replacement', $args); + assertInstanceOf(stdClass::class, $args['filter']); + assertInstanceOf(stdClass::class, $args['replacement']); + return $collection->{$this->name}( $args['filter'], $args['replacement'], array_diff_key($args, ['filter' => 1, 'replacement' => 1]) ); + case 'findOneAndUpdate': if (isset($args['returnDocument'])) { $args['returnDocument'] = strtolower($args['returnDocument']); @@ -351,79 +493,207 @@ private function executeForCollection(Collection $collection) : FindOneAndUpdate::RETURN_DOCUMENT_BEFORE; } // Fall through - case 'updateMany': case 'updateOne': + assertArrayHasKey('filter', $args); + assertArrayHasKey('update', $args); + assertInstanceOf(stdClass::class, $args['filter']); + assertThat($args['update'], logicalOr(new IsType('array'), new IsType('object'))); + return $collection->{$this->name}( $args['filter'], $args['update'], array_diff_key($args, ['filter' => 1, 'update' => 1]) ); + case 'insertMany': - // Merge nested and top-level options (see: SPEC-1158) - $options = isset($args['options']) ? (array) $args['options'] : []; - $options += array_diff_key($args, ['documents' => 1]); + assertArrayHasKey('documents', $args); + assertIsArray($args['documents']); return $collection->insertMany( $args['documents'], - $options + array_diff_key($args, ['documents' => 1]) ); + case 'insertOne': + assertArrayHasKey('document', $args); + assertInstanceOf(stdClass::class, $args['document']); + return $collection->insertOne( $args['document'], array_diff_key($args, ['document' => 1]) ); + case 'listIndexes': - return $collection->listIndexes($args); + return array_map( + function (IndexInfo $info) { + return $info->__debugInfo(); + }, + iterator_to_array($collection->listIndexes($args)) + ); + case 'mapReduce': + assertArrayHasKey('map', $args); + assertArrayHasKey('reduce', $args); + assertArrayHasKey('out', $args); + assertInstanceOf(Javascript::class, $args['map']); + assertInstanceOf(Javascript::class, $args['reduce']); + assertIsString($args['out']); + return $collection->mapReduce( $args['map'], $args['reduce'], $args['out'], array_diff_key($args, ['map' => 1, 'reduce' => 1, 'out' => 1]) ); + + case 'rename': + assertArrayHasKey('to', $args); + assertIsString($args['to']); + + return $collection->rename( + $args['to'], + null, /* $toDatabaseName */ + array_diff_key($args, ['to' => 1]) + ); + default: Assert::fail('Unsupported collection operation: ' . $this->name); } } + private function executeForCursor(Cursor $cursor) + { + $args = $this->prepareArguments(); + Util::assertArgumentsBySchema(Cursor::class, $this->name, $args); + + switch ($this->name) { + case 'close': + /* PHPC does not provide an API to directly close a cursor. + * mongoc_cursor_destroy is only invoked from the Cursor's + * free_object handler, which requires unsetting the object from + * the entity map to trigger garbage collection. This will need + * a different approach if tests ever attempt to access the + * cursor entity after calling the "close" operation. */ + $this->entityMap->closeCursor($this->object); + assertFalse($this->entityMap->offsetExists($this->object)); + break; + case 'iterateUntilDocumentOrError': + /* Note: the first iteration should use rewind, otherwise we may + * miss a document from the initial batch (possible if using a + * resume token). We can infer this from a null key; however, + * if a test ever calls this operation consecutively to expect + * multiple errors from the same ChangeStream we will need a + * different approach (e.g. examining internal hasAdvanced + * property on the ChangeStream). */ + + /* Note: similar to iterateUntilDocumentOrError for ChangeStream + * entities, a different approach will be needed if a test ever + * calls this operation consecutively to expect multiple errors. + */ + if ($cursor->key() === null) { + $cursor->rewind(); + + if ($cursor->valid()) { + return $cursor->current(); + } + } + + do { + $cursor->next(); + } while (! $cursor->valid()); + + return $cursor->current(); + + default: + Assert::fail('Unsupported cursor operation: ' . $this->name); + } + } + private function executeForDatabase(Database $database) { $args = $this->prepareArguments(); + Util::assertArgumentsBySchema(Database::class, $this->name, $args); switch ($this->name) { case 'aggregate': - return $database->aggregate( + assertArrayHasKey('pipeline', $args); + assertIsArray($args['pipeline']); + + return iterator_to_array($database->aggregate( $args['pipeline'], array_diff_key($args, ['pipeline' => 1]) - ); + )); + case 'createChangeStream': - $changeStream = $database->watch( - $args['pipeline'] ?? [], + assertArrayHasKey('pipeline', $args); + assertIsArray($args['pipeline']); + + return $database->watch( + $args['pipeline'], array_diff_key($args, ['pipeline' => 1]) ); - $changeStream->rewind(); - return $changeStream; case 'createCollection': + assertArrayHasKey('collection', $args); + assertIsString($args['collection']); + return $database->createCollection( $args['collection'], array_diff_key($args, ['collection' => 1]) ); + case 'dropCollection': + assertArrayHasKey('collection', $args); + assertIsString($args['collection']); + return $database->dropCollection( $args['collection'], array_diff_key($args, ['collection' => 1]) ); + case 'listCollectionNames': - return $database->listCollectionNames($args); + return iterator_to_array($database->listCollectionNames($args)); + case 'listCollections': - return $database->listCollections($args); + return array_map( + function (CollectionInfo $info) { + return $info->__debugInfo(); + }, + iterator_to_array($database->listCollections($args)) + ); + + case 'modifyCollection': + assertArrayHasKey('collection', $args); + assertIsString($args['collection']); + + /* ModifyCollection takes collection and command options + * separately, so we must split the array after initially + * filtering out the collection name. + * + * The typeMap option is intentionally omitted since it is + * specific to PHPLIB and will never appear in spec tests. */ + $options = array_diff_key($args, ['collection' => 1]); + $collectionOptions = array_diff_key($options, ['session' => 1, 'writeConcern' => 1]); + $options = array_intersect_key($options, ['session' => 1, 'writeConcern' => 1]); + + return $database->modifyCollection($args['collection'], $collectionOptions, $options); + case 'runCommand': - return $database->command( - $args['command'], - array_diff_key($args, ['command' => 1]) - )->toArray()[0]; + assertArrayHasKey('command', $args); + assertInstanceOf(stdClass::class, $args['command']); + + // Note: commandName is not used by PHP + $options = array_diff_key($args, ['command' => 1, 'commandName' => 1]); + + /* runCommand spec tests may execute commands that return a + * cursor (e.g. aggregate). PHPC creates a cursor automatically + * so cannot return the original command result. Existing spec + * tests do not evaluate the result, so we can return null here. + * If that changes down the line, we can use command monitoring + * to capture and return the command result. */ + return $database->command($args['command'], $options)->toArray()[0] ?? null; + default: Assert::fail('Unsupported database operation: ' . $this->name); } @@ -432,17 +702,23 @@ private function executeForDatabase(Database $database) private function executeForSession(Session $session) { $args = $this->prepareArguments(); + Util::assertArgumentsBySchema(Session::class, $this->name, $args); switch ($this->name) { case 'abortTransaction': return $session->abortTransaction(); + case 'commitTransaction': return $session->commitTransaction(); + case 'endSession': return $session->endSession(); + case 'startTransaction': return $session->startTransaction($args); + case 'withTransaction': + assertArrayHasKey('callback', $args); assertIsArray($args['callback']); $operations = array_map(function ($o) { @@ -451,13 +727,14 @@ private function executeForSession(Session $session) return new Operation($o, $this->context); }, $args['callback']); - $callback = function () use ($operations) { + $callback = function () use ($operations): void { foreach ($operations as $operation) { $operation->assert(true); // rethrow exceptions } }; return with_transaction($session, $callback, array_diff_key($args, ['callback' => 1])); + default: Assert::fail('Unsupported session operation: ' . $this->name); } @@ -466,18 +743,30 @@ private function executeForSession(Session $session) private function executeForBucket(Bucket $bucket) { $args = $this->prepareArguments(); + Util::assertArgumentsBySchema(Bucket::class, $this->name, $args); switch ($this->name) { case 'delete': + assertArrayHasKey('id', $args); + return $bucket->delete($args['id']); + case 'downloadByName': - return stream_get_contents($bucket->openDownloadStream( + assertArrayHasKey('filename', $args); + assertIsString($args['filename']); + + return stream_get_contents($bucket->openDownloadStreamByName( $args['filename'], array_diff_key($args, ['filename' => 1]) )); + case 'download': + assertArrayHasKey('id', $args); + return stream_get_contents($bucket->openDownloadStream($args['id'])); + case 'uploadWithId': + assertArrayHasKey('id', $args); $args['_id'] = $args['id']; unset($args['id']); @@ -491,6 +780,7 @@ private function executeForBucket(Bucket $bucket) $args['source'], array_diff_key($args, ['filename' => 1, 'source' => 1]) ); + default: Assert::fail('Unsupported bucket operation: ' . $this->name); } @@ -499,80 +789,122 @@ private function executeForBucket(Bucket $bucket) private function executeForTestRunner() { $args = $this->prepareArguments(); - - if (array_key_exists('client', $args)) { - assertIsString($args['client']); - $args['client'] = $this->entityMap->getClient($args['client']); - } + Util::assertArgumentsBySchema(self::OBJECT_TEST_RUNNER, $this->name, $args); switch ($this->name) { case 'assertCollectionExists': + assertArrayHasKey('databaseName', $args); + assertArrayHasKey('collectionName', $args); assertIsString($args['databaseName']); assertIsString($args['collectionName']); $database = $this->context->getInternalClient()->selectDatabase($args['databaseName']); assertContains($args['collectionName'], $database->listCollectionNames()); break; case 'assertCollectionNotExists': + assertArrayHasKey('databaseName', $args); + assertArrayHasKey('collectionName', $args); assertIsString($args['databaseName']); assertIsString($args['collectionName']); $database = $this->context->getInternalClient()->selectDatabase($args['databaseName']); assertNotContains($args['collectionName'], $database->listCollectionNames()); break; case 'assertIndexExists': + assertArrayHasKey('databaseName', $args); + assertArrayHasKey('collectionName', $args); + assertArrayHasKey('indexName', $args); assertIsString($args['databaseName']); assertIsString($args['collectionName']); assertIsString($args['indexName']); assertContains($args['indexName'], $this->getIndexNames($args['databaseName'], $args['collectionName'])); break; case 'assertIndexNotExists': + assertArrayHasKey('databaseName', $args); + assertArrayHasKey('collectionName', $args); + assertArrayHasKey('indexName', $args); assertIsString($args['databaseName']); assertIsString($args['collectionName']); assertIsString($args['indexName']); assertNotContains($args['indexName'], $this->getIndexNames($args['databaseName'], $args['collectionName'])); break; case 'assertSameLsidOnLastTwoCommands': + /* Context::getEventObserverForClient() requires the client ID. + * Avoid checking $args['client'], which is already resolved. */ + assertArrayHasKey('client', $this->arguments); $eventObserver = $this->context->getEventObserverForClient($this->arguments['client']); assertEquals(...$eventObserver->getLsidsOnLastTwoCommands()); break; case 'assertDifferentLsidOnLastTwoCommands': + /* Context::getEventObserverForClient() requires the client ID. + * Avoid checking $args['client'], which is already resolved. */ + assertArrayHasKey('client', $this->arguments); $eventObserver = $this->context->getEventObserverForClient($this->arguments['client']); assertNotEquals(...$eventObserver->getLsidsOnLastTwoCommands()); break; + case 'assertNumberConnectionsCheckedOut': + assertArrayHasKey('connections', $args); + assertIsInt($args['connections']); + /* PHP does not implement connection pooling. Check parameters + * for the sake of valid-fail tests, but otherwise raise an + * error. */ + Assert::fail('Tests using assertNumberConnectionsCheckedOut should be skipped'); + break; case 'assertSessionDirty': - assertTrue($this->context->isDirtySession($this->arguments['session'])); + assertArrayHasKey('session', $args); + assertTrue($args['session']->isDirty()); break; case 'assertSessionNotDirty': - assertFalse($this->context->isDirtySession($this->arguments['session'])); + assertArrayHasKey('session', $args); + assertFalse($args['session']->isDirty()); break; case 'assertSessionPinned': + assertArrayHasKey('session', $args); assertInstanceOf(Session::class, $args['session']); assertInstanceOf(Server::class, $args['session']->getServer()); break; case 'assertSessionTransactionState': + assertArrayHasKey('session', $args); assertInstanceOf(Session::class, $args['session']); assertSame($this->arguments['state'], $args['session']->getTransactionState()); break; case 'assertSessionUnpinned': + assertArrayHasKey('session', $args); assertInstanceOf(Session::class, $args['session']); assertNull($args['session']->getServer()); break; case 'failPoint': + assertArrayHasKey('client', $args); + assertArrayHasKey('failPoint', $args); + assertInstanceOf(Client::class, $args['client']); assertInstanceOf(stdClass::class, $args['failPoint']); $args['client']->selectDatabase('admin')->command($args['failPoint']); break; case 'targetedFailPoint': + assertArrayHasKey('session', $args); + assertArrayHasKey('failPoint', $args); assertInstanceOf(Session::class, $args['session']); assertInstanceOf(stdClass::class, $args['failPoint']); assertNotNull($args['session']->getServer(), 'Session is pinned'); $operation = new DatabaseCommand('admin', $args['failPoint']); $operation->execute($args['session']->getServer()); break; + case 'loop': + assertArrayHasKey('operations', $args); + assertIsArray($args['operations']); + + $operations = array_map(function ($o) { + assertIsObject($o); + + return new Operation($o, $this->context); + }, $args['operations']); + + return (new Loop($operations, $this->context, array_diff_key($args, ['operations' => 1])))->execute(); + default: Assert::fail('Unsupported test runner operation: ' . $this->name); } } - private function getIndexNames(string $databaseName, string $collectionName) : array + private function getIndexNames(string $databaseName, string $collectionName): array { return array_map( function (IndexInfo $indexInfo) { @@ -582,10 +914,15 @@ function (IndexInfo $indexInfo) { ); } - private function prepareArguments() : array + private function prepareArguments(): array { $args = $this->arguments; + if (array_key_exists('client', $args)) { + assertIsString($args['client']); + $args['client'] = $this->entityMap->getClient($args['client']); + } + if (array_key_exists('session', $args)) { assertIsString($args['session']); $args['session'] = $this->entityMap->getSession($args['session']); @@ -595,7 +932,7 @@ private function prepareArguments() : array return Util::prepareCommonOptions($args); } - private static function prepareBulkWriteRequest(stdClass $request) : array + private static function prepareBulkWriteRequest(stdClass $request): array { $request = (array) $request; assertCount(1, $request); @@ -608,15 +945,27 @@ private static function prepareBulkWriteRequest(stdClass $request) : array switch ($type) { case 'deleteMany': case 'deleteOne': + assertArrayHasKey('filter', $args); + assertInstanceOf(stdClass::class, $args['filter']); + return [ $type => [ $args['filter'], array_diff_key($args, ['filter' => 1]), ], ]; + case 'insertOne': - return [ 'insertOne' => [ $args['document']]]; + assertArrayHasKey('document', $args); + + return ['insertOne' => [$args['document']]]; + case 'replaceOne': + assertArrayHasKey('filter', $args); + assertArrayHasKey('replacement', $args); + assertInstanceOf(stdClass::class, $args['filter']); + assertInstanceOf(stdClass::class, $args['replacement']); + return [ 'replaceOne' => [ $args['filter'], @@ -624,8 +973,14 @@ private static function prepareBulkWriteRequest(stdClass $request) : array array_diff_key($args, ['filter' => 1, 'replacement' => 1]), ], ]; + case 'updateMany': case 'updateOne': + assertArrayHasKey('filter', $args); + assertArrayHasKey('update', $args); + assertInstanceOf(stdClass::class, $args['filter']); + assertThat($args['update'], logicalOr(new IsType('array'), new IsType('object'))); + return [ $type => [ $args['filter'], @@ -633,12 +988,38 @@ private static function prepareBulkWriteRequest(stdClass $request) : array array_diff_key($args, ['filter' => 1, 'update' => 1]), ], ]; + default: Assert::fail('Unsupported bulk write request: ' . $type); } } - private static function prepareUploadArguments(array $args) : array + /** + * ClientEncryption::rewrapManyDataKey() returns its result as a raw BSON + * document and does not utilize WriteResult because getServer() cannot be + * implemented. To satisfy result expectations, unset bulkWriteResult if it + * is null and rename its fields (per the CRUD spec) otherwise. */ + private static function prepareRewrapManyDataKeyResult(stdClass $result): object + { + if ($result->bulkWriteResult === null) { + unset($result->bulkWriteResult); + + return $result; + } + + $result->bulkWriteResult = [ + 'insertedCount' => $result->bulkWriteResult->nInserted, + 'matchedCount' => $result->bulkWriteResult->nMatched, + 'modifiedCount' => $result->bulkWriteResult->nModified, + 'deletedCount' => $result->bulkWriteResult->nRemoved, + 'upsertedCount' => $result->bulkWriteResult->nUpserted, + 'upsertedIds' => $result->bulkWriteResult->upserted ?? new stdClass(), + ]; + + return $result; + } + + private static function prepareUploadArguments(array $args): array { $source = $args['source'] ?? null; assertIsObject($source); diff --git a/tests/UnifiedSpecTests/RunOnRequirement.php b/tests/UnifiedSpecTests/RunOnRequirement.php index 909d3c1ab..4290bda9d 100644 --- a/tests/UnifiedSpecTests/RunOnRequirement.php +++ b/tests/UnifiedSpecTests/RunOnRequirement.php @@ -2,22 +2,34 @@ namespace MongoDB\Tests\UnifiedSpecTests; +use MongoDB\Tests\UnifiedSpecTests\Constraint\Matches; use stdClass; + +use function array_diff; use function in_array; +use function PHPUnit\Framework\assertContains; use function PHPUnit\Framework\assertContainsOnly; +use function PHPUnit\Framework\assertEmpty; use function PHPUnit\Framework\assertIsArray; +use function PHPUnit\Framework\assertIsBool; +use function PHPUnit\Framework\assertIsObject; use function PHPUnit\Framework\assertIsString; use function PHPUnit\Framework\assertMatchesRegularExpression; use function version_compare; class RunOnRequirement { - const TOPOLOGY_SINGLE = 'single'; - const TOPOLOGY_REPLICASET = 'replicaset'; - const TOPOLOGY_SHARDED = 'sharded'; - const TOPOLOGY_SHARDED_REPLICASET = 'sharded-replicaset'; + public const TOPOLOGY_SINGLE = 'single'; + public const TOPOLOGY_REPLICASET = 'replicaset'; + public const TOPOLOGY_SHARDED = 'sharded'; + public const TOPOLOGY_SHARDED_REPLICASET = 'sharded-replicaset'; + public const TOPOLOGY_LOAD_BALANCED = 'load-balanced'; + + public const SERVERLESS_REQUIRE = 'require'; + public const SERVERLESS_FORBID = 'forbid'; + public const SERVERLESS_ALLOW = 'allow'; - const VERSION_PATTERN = '/^[0-9]+(\\.[0-9]+){1,2}$/'; + public const VERSION_PATTERN = '/^[0-9]+(\\.[0-9]+){1,2}$/'; /** @var string */ private $minServerVersion; @@ -28,8 +40,38 @@ class RunOnRequirement /** @var array */ private $topologies; + /** @var stdClass */ + private $serverParameters; + + /** @var bool */ + private $auth; + + /** @var string */ + private $serverless; + + /** @var bool */ + private $csfle; + + /** @var array */ + private static $supportedTopologies = [ + self::TOPOLOGY_SINGLE, + self::TOPOLOGY_REPLICASET, + self::TOPOLOGY_SHARDED, + self::TOPOLOGY_SHARDED_REPLICASET, + self::TOPOLOGY_LOAD_BALANCED, + ]; + + /** @var array */ + private static $supportedServerless = [ + self::SERVERLESS_REQUIRE, + self::SERVERLESS_FORBID, + self::SERVERLESS_ALLOW, + ]; + public function __construct(stdClass $o) { + Util::assertHasOnlyKeys($o, ['minServerVersion', 'maxServerVersion', 'topologies', 'serverParameters', 'auth', 'serverless', 'csfle']); + if (isset($o->minServerVersion)) { assertIsString($o->minServerVersion); assertMatchesRegularExpression(self::VERSION_PATTERN, $o->minServerVersion); @@ -45,11 +87,33 @@ public function __construct(stdClass $o) if (isset($o->topologies)) { assertIsArray($o->topologies); assertContainsOnly('string', $o->topologies); + assertEmpty(array_diff($o->topologies, self::$supportedTopologies)); $this->topologies = $o->topologies; } + + if (isset($o->serverParameters)) { + assertIsObject($o->serverParameters); + $this->serverParameters = $o->serverParameters; + } + + if (isset($o->auth)) { + assertIsBool($o->auth); + $this->auth = $o->auth; + } + + if (isset($o->serverless)) { + assertIsString($o->serverless); + assertContains($o->serverless, self::$supportedServerless); + $this->serverless = $o->serverless; + } + + if (isset($o->csfle)) { + assertIsBool($o->csfle); + $this->csfle = $o->csfle; + } } - public function isSatisfied(string $serverVersion, string $topology) : bool + public function isSatisfied(string $serverVersion, string $topology, ServerParameterHelper $serverParameters, bool $isAuthenticated, bool $isServerless, bool $isClientSideEncryptionSupported): bool { if (isset($this->minServerVersion) && version_compare($serverVersion, $this->minServerVersion, '<')) { return false; @@ -59,20 +123,52 @@ public function isSatisfied(string $serverVersion, string $topology) : bool return false; } - if (isset($this->topologies)) { - if (in_array($topology, $this->topologies)) { - return true; + if (isset($this->topologies) && ! $this->isTopologySatisfied($topology)) { + return false; + } + + if (isset($this->serverParameters)) { + foreach ($this->serverParameters as $parameter => $expectedValue) { + $constraint = new Matches($expectedValue, null, true, false); + if (! $constraint->evaluate($serverParameters->$parameter, '', true)) { + return false; + } + } + } + + if (isset($this->auth) && $isAuthenticated !== $this->auth) { + return false; + } + + if (isset($this->serverless)) { + if (! $isServerless && $this->serverless === self::SERVERLESS_REQUIRE) { + return false; } - /* Ensure "sharded-replicaset" is also accepted for topologies that - * only include "sharded" (agnostic about the shard topology) */ - if ($topology === self::TOPOLOGY_SHARDED_REPLICASET && in_array(self::TOPOLOGY_SHARDED, $this->topologies)) { - return true; + if ($isServerless && $this->serverless === self::SERVERLESS_FORBID) { + return false; } + } + if (isset($this->csfle) && $isClientSideEncryptionSupported !== $this->csfle) { return false; } return true; } + + private function isTopologySatisfied(string $topology): bool + { + if (in_array($topology, $this->topologies)) { + return true; + } + + /* Ensure "sharded-replicaset" is also accepted for topologies that + * only include "sharded" (agnostic about the shard topology) */ + if ($topology === self::TOPOLOGY_SHARDED_REPLICASET && in_array(self::TOPOLOGY_SHARDED, $this->topologies)) { + return true; + } + + return false; + } } diff --git a/tests/UnifiedSpecTests/ServerParameterHelper.php b/tests/UnifiedSpecTests/ServerParameterHelper.php new file mode 100644 index 000000000..6e14e8faa --- /dev/null +++ b/tests/UnifiedSpecTests/ServerParameterHelper.php @@ -0,0 +1,99 @@ + */ + private $parameters = []; + + /** @var bool */ + private $fetchAllParametersFailed = false; + + /** @var bool */ + private $allParametersFetched = false; + + public function __construct(Client $client) + { + $this->client = $client; + } + + /** @return mixed */ + public function __get(string $parameter) + { + if (! array_key_exists($parameter, $this->parameters)) { + $this->fetchParameter($parameter); + } + + return $this->parameters[$parameter]; + } + + private function fetchParameter(string $parameter): void + { + // Try fetching all parameters once + if (! $this->allParametersFetched && ! $this->fetchAllParametersFailed) { + $this->fetchAllParameters(); + } + + if (array_key_exists($parameter, $this->parameters)) { + return; + } + + // If fetching all parameters failed, or the parameter was not part of + // the list, fetch the single parameter as fallback + $this->fetchSingleParameter($parameter); + } + + private function fetchAllParameters(): void + { + try { + $database = $this->client->selectDatabase('admin'); + $cursor = $database->command( + ['getParameter' => '*'], + [ + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), + 'typeMap' => [ + 'root' => 'array', + 'document' => 'array', + 'array' => 'array', + ], + ] + ); + + $this->parameters = $cursor->toArray()[0]; + $this->allParametersFetched = true; + } catch (CommandException $e) { + $this->fetchAllParametersFailed = true; + } + } + + private function fetchSingleParameter(string $parameter): void + { + $database = $this->client->selectDatabase('admin'); + $cursor = $database->command( + [ + 'getParameter' => 1, + $parameter => 1, + ], + [ + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), + 'typeMap' => [ + 'root' => 'array', + 'document' => 'array', + 'array' => 'array', + ], + ] + ); + + $this->parameters[$parameter] = $cursor->toArray()[0][$parameter]; + } +} diff --git a/tests/UnifiedSpecTests/UnifiedSpecTest.php b/tests/UnifiedSpecTests/UnifiedSpecTest.php index 5090692fe..730e59e0d 100644 --- a/tests/UnifiedSpecTests/UnifiedSpecTest.php +++ b/tests/UnifiedSpecTests/UnifiedSpecTest.php @@ -2,387 +2,286 @@ namespace MongoDB\Tests\UnifiedSpecTests; -use MongoDB\Client; -use MongoDB\Collection; -use MongoDB\Driver\Exception\ServerException; -use MongoDB\Driver\ReadPreference; -use MongoDB\Driver\Server; -use MongoDB\Operation\DatabaseCommand; +use Exception; +use Generator; use MongoDB\Tests\FunctionalTestCase; -use stdClass; -use Symfony\Bridge\PhpUnit\SetUpTearDownTrait; -use Throwable; -use function file_get_contents; -use function gc_collect_cycles; +use PHPUnit\Framework\SkippedTest; +use PHPUnit\Framework\Warning; + +use function basename; +use function dirname; use function glob; -use function MongoDB\BSON\fromJSON; -use function MongoDB\BSON\toPHP; -use function PHPUnit\Framework\assertTrue; -use function sprintf; -use function version_compare; /** - * Unified spec test runner. + * Unified test format spec tests. * - * @see https://github.com/mongodb/specifications/pull/846 + * @see https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst + * @group serverless */ class UnifiedSpecTest extends FunctionalTestCase { - use SetUpTearDownTrait; - - const SERVER_ERROR_INTERRUPTED = 11601; - - const MIN_SCHEMA_VERSION = '1.0'; - const MAX_SCHEMA_VERSION = '1.1'; - - const TOPOLOGY_SINGLE = 'single'; - const TOPOLOGY_REPLICASET = 'replicaset'; - const TOPOLOGY_SHARDED = 'sharded'; - const TOPOLOGY_SHARDED_REPLICASET = 'sharded-replicaset'; - - /** @var MongoDB\Client */ - private static $internalClient; - - /** @var FailPointObserver */ - private $failPointObserver; - - private static function doSetUpBeforeClass() + /** @var array */ + private static $incompleteTests = [ + // Many load balancer tests use CMAP events and/or assertNumberConnectionsCheckedOut + 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: no connection is pinned if all documents are returned in the initial batch' => 'PHPC does not implement CMAP', + 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: pinned connections are returned when the cursor is drained' => 'PHPC does not implement CMAP', + 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: pinned connections are returned to the pool when the cursor is closed' => 'PHPC does not implement CMAP', + 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: pinned connections are not returned after an network error during getMore' => 'PHPC does not implement CMAP', + 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: pinned connections are returned after a network error during a killCursors request' => 'PHPC does not implement CMAP', + 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: pinned connections are not returned to the pool after a non-network error on getMore' => 'PHPC does not implement CMAP', + 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: aggregate pins the cursor to a connection' => 'PHPC does not implement CMAP', + 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: listCollections pins the cursor to a connection' => 'PHPC does not implement CMAP', + 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: listIndexes pins the cursor to a connection' => 'PHPC does not implement CMAP', + 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: change streams pin to a connection' => 'PHPC does not implement CMAP', + 'load-balancers/monitoring events include correct fields: poolClearedEvent events include serviceId' => 'PHPC does not implement CMAP', + 'load-balancers/state change errors are correctly handled: only connections for a specific serviceId are closed when pools are cleared' => 'PHPC does not implement CMAP', + 'load-balancers/state change errors are correctly handled: errors during the initial connection hello are ignored' => 'PHPC does not implement CMAP', + 'load-balancers/state change errors are correctly handled: errors during authentication are processed' => 'PHPC does not implement CMAP', + 'load-balancers/state change errors are correctly handled: stale errors are ignored' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: all operations go to the same mongos' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: transaction can be committed multiple times' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is not released after a non-transient CRUD error' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is not released after a non-transient commit error' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a non-transient abort error' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient non-network CRUD error' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient network CRUD error' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient non-network commit error' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient network commit error' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient non-network abort error' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient network abort error' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released on successful abort' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is returned when a new transaction is started' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is returned when a non-transaction operation uses the session' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: a connection can be shared by a transaction and a cursor' => 'PHPC does not implement CMAP', + 'load-balancers/wait queue timeout errors include details about checked out connections: wait queue timeout errors include cursor statistics' => 'PHPC does not implement CMAP', + 'load-balancers/wait queue timeout errors include details about checked out connections: wait queue timeout errors include transaction statistics' => 'PHPC does not implement CMAP', + // PHPC does not implement CMAP + 'valid-pass/assertNumberConnectionsCheckedOut: basic assertion succeeds' => 'PHPC does not implement CMAP', + 'valid-pass/entity-client-cmap-events: events are captured during an operation' => 'PHPC does not implement CMAP', + 'valid-pass/expectedEventsForClient-eventType: eventType can be set to command and cmap' => 'PHPC does not implement CMAP', + 'valid-pass/expectedEventsForClient-eventType: eventType defaults to command if unset' => 'PHPC does not implement CMAP', + // CSOT is not yet implemented + 'valid-pass/collectionData-createOptions: collection is created with the correct options' => 'CSOT is not yet implemented (PHPC-1760)', + 'valid-pass/createEntities-operation: createEntities operation' => 'CSOT is not yet implemented (PHPC-1760)', + 'valid-pass/entity-cursor-iterateOnce: iterateOnce' => 'CSOT is not yet implemented (PHPC-1760)', + 'valid-pass/matches-lte-operator: special lte matching operator' => 'CSOT is not yet implemented (PHPC-1760)', + ]; + + /** @var UnifiedTestRunner */ + private static $runner; + + public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); - /* Provide internal client unmodified URI, since it may need to execute - * commands on multiple mongoses (e.g. killAllSessions) */ - self::$internalClient = new Client(static::getUri(true)); - self::killAllSessions(); + /* Provide unmodified URI for internal client, since it may need to + * execute commands on multiple mongoses (e.g. killAllSessions) */ + self::$runner = new UnifiedTestRunner(static::getUri(true)); } - private function doSetUp() + public function setUp(): void { parent::setUp(); - /* The transactions spec advises calling killAllSessions only at the - * start of the test suite and after failed tests; however, the "unpin - * after transient error within a transaction" pinning test causes the - * subsequent transaction test to block. */ - self::killAllSessions(); - - $this->failPointObserver = new FailPointObserver(); - $this->failPointObserver->start(); + if (isset(self::$incompleteTests[$this->dataDescription()])) { + $this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]); + } } - private function doTearDown() + /** @dataProvider provideChangeStreamsTests */ + public function testChangeStreams(UnifiedTestCase $test): void { - if ($this->hasFailed()) { - self::killAllSessions(); - } - - $this->failPointObserver->stop(); - $this->failPointObserver->disableFailPoints(); - - /* Manually invoking garbage collection since each test is prone to - * create cycles (perhaps due to EntityMap), which can leak and prevent - * sessions from being released back into the pool. */ - gc_collect_cycles(); + self::$runner->run($test); + } - parent::tearDown(); + public function provideChangeStreamsTests() + { + return $this->provideTests(__DIR__ . '/change-streams/*.json'); } /** - * @dataProvider providePassingTests + * @dataProvider provideClientSideEncryptionTests + * @group csfle */ - public function testPassingTests(stdClass $test, string $schemaVersion, array $runOnRequirements = null, array $createEntities = null, array $initialData = null) + public function testClientSideEncryption(UnifiedTestCase $test): void { - if (! $this->isSchemaVersionSupported($schemaVersion)) { - $this->markTestIncomplete(sprintf('Test format schema version "%s" is not supported', $schemaVersion)); - } - - if (isset($runOnRequirements)) { - $this->checkRunOnRequirements($runOnRequirements); - } - - if (isset($test->skipReason)) { - $this->assertIsString($test->skipReason); - $this->markTestSkipped($test->skipReason); - } - - if (isset($test->runOnRequirements)) { - $this->assertIsArray($test->runOnRequirements); - $this->checkRunOnRequirements($test->runOnRequirements); - } - - if (isset($initialData)) { - $this->prepareInitialData($initialData); - } - - // Give Context unmodified URI so it can enforce useMultipleMongoses - $context = new Context(self::$internalClient, static::getUri(true)); - - if (isset($createEntities)) { - $context->createEntities($createEntities); - } - - $this->assertIsArray($test->operations); - $this->preventStaleDbVersionError($test->operations, $context); - - $context->startEventObservers(); - - foreach ($test->operations as $o) { - $operation = new Operation($o, $context); - $operation->assert(); - } - - $context->stopEventObservers(); - - if (isset($test->expectEvents)) { - $this->assertIsArray($test->expectEvents); - $context->assertExpectedEventsForClients($test->expectEvents); - } - - if (isset($test->outcome)) { - $this->assertIsArray($test->outcome); - $this->assertOutcome($test->outcome); - } + self::$runner->run($test); } - public function providePassingTests() + public function provideClientSideEncryptionTests() { - return $this->provideTests(__DIR__ . '/valid-pass'); + return $this->provideTests(__DIR__ . '/client-side-encryption/*.json'); } - /** - * @dataProvider provideFailingTests - */ - public function testFailingTests(...$args) + /** @dataProvider provideCollectionManagementTests */ + public function testCollectionManagement(UnifiedTestCase $test): void { - // Cannot use expectException(), as it ignores PHPUnit Exceptions - $failed = false; - - try { - $this->testCase(...$args); - } catch (Throwable $e) { - $failed = true; - } - - assertTrue($failed, 'Expected test to throw an exception'); + self::$runner->run($test); } - public function provideFailingTests() + public function provideCollectionManagementTests() { - return $this->provideTests(__DIR__ . '/valid-fail'); + return $this->provideTests(__DIR__ . '/collection-management/*.json'); } - private function provideTests(string $dir) + /** @dataProvider provideCommandMonitoringTests */ + public function testCommandMonitoring(UnifiedTestCase $test): void { - $testArgs = []; - - foreach (glob($dir . '/*.json') as $filename) { - /* Decode the file through the driver's extended JSON parser to - * ensure proper handling of special types. */ - $json = toPHP(fromJSON(file_get_contents($filename))); - - $description = $json->description; - $schemaVersion = $json->schemaVersion; - $runOnRequirements = $json->runOnRequirements ?? null; - $createEntities = $json->createEntities ?? null; - $initialData = $json->initialData ?? null; - $tests = $json->tests; - - /* Assertions in data providers do not count towards test assertions - * but failures will interrupt the test suite with a warning. */ - $message = 'Invalid test file: ' . $filename; - $this->assertIsString($description, $message); - $this->assertIsString($schemaVersion, $message); - $this->assertIsArray($tests, $message); - - foreach ($json->tests as $test) { - $this->assertIsObject($test, $message); - $this->assertIsString($test->description, $message); - - $name = $description . ': ' . $test->description; - $testArgs[$name] = [$test, $schemaVersion, $runOnRequirements, $createEntities, $initialData]; - } - } - - return $testArgs; + self::$runner->run($test); } - /** - * Checks server version and topology requirements. - * - * @param array $runOnRequirements - * @throws SkippedTest unless one or more runOnRequirements are met - */ - private function checkRunOnRequirements(array $runOnRequirements) + public function provideCommandMonitoringTests() { - $this->assertNotEmpty($runOnRequirements); - $this->assertContainsOnly('object', $runOnRequirements); - - $serverVersion = $this->getCachedServerVersion(); - $topology = $this->getCachedTopology(); + return $this->provideTests(__DIR__ . '/command-monitoring/*.json'); + } - foreach ($runOnRequirements as $o) { - $runOnRequirement = new RunOnRequirement($o); - if ($runOnRequirement->isSatisfied($serverVersion, $topology)) { - return; - } - } + /** @dataProvider provideCrudTests */ + public function testCrud(UnifiedTestCase $test): void + { + self::$runner->run($test); + } - $this->markTestSkipped(sprintf('Server version "%s" and topology "%s" do not meet test requirements', $serverVersion, $topology)); + public function provideCrudTests() + { + return $this->provideTests(__DIR__ . '/crud/*.json'); } - /** - * Return the server version (cached for subsequent calls). - * - * @return string - */ - private function getCachedServerVersion() + /** @dataProvider provideGridFSTests */ + public function testGridFS(UnifiedTestCase $test): void { - static $cachedServerVersion; + self::$runner->run($test); + } - if (isset($cachedServerVersion)) { - return $cachedServerVersion; - } + public function provideGridFSTests() + { + return $this->provideTests(__DIR__ . '/gridfs/*.json'); + } - $cachedServerVersion = $this->getServerVersion(); + /** @dataProvider provideLoadBalancers */ + public function testLoadBalancers(UnifiedTestCase $test): void + { + self::$runner->run($test); + } - return $cachedServerVersion; + public function provideLoadBalancers() + { + return $this->provideTests(__DIR__ . '/load-balancers/*.json'); } - /** - * Return the topology type (cached for subsequent calls). - * - * @return string - * @throws UnexpectedValueException if topology is neither single nor RS nor sharded - */ - private function getCachedTopology() + /** @dataProvider provideRetryableWritesTests */ + public function testRetryableWrites(UnifiedTestCase $test): void { - static $cachedTopology = null; + self::$runner->run($test); + } - if (isset($cachedTopology)) { - return $cachedTopology; - } + public function provideRetryableWritesTests() + { + return $this->provideTests(__DIR__ . '/retryable-writes/*.json'); + } - switch ($this->getPrimaryServer()->getType()) { - case Server::TYPE_STANDALONE: - $cachedTopology = RunOnRequirement::TOPOLOGY_SINGLE; - break; + /** @dataProvider provideRunCommandTests */ + public function testRunCommand(UnifiedTestCase $test): void + { + self::$runner->run($test); + } - case Server::TYPE_RS_PRIMARY: - $cachedTopology = RunOnRequirement::TOPOLOGY_REPLICASET; - break; + public function provideRunCommandTests() + { + return $this->provideTests(__DIR__ . '/run-command/*.json'); + } - case Server::TYPE_MONGOS: - $cachedTopology = $this->isShardedClusterUsingReplicasets() - ? RunOnRequirement::TOPOLOGY_SHARDED_REPLICASET - : RunOnRequirement::TOPOLOGY_SHARDED; - break; + /** @dataProvider provideSessionsTests */ + public function testSessions(UnifiedTestCase $test): void + { + self::$runner->run($test); + } - default: - throw new UnexpectedValueException('Toplogy is neither single nor RS nor sharded'); - } + public function provideSessionsTests() + { + return $this->provideTests(__DIR__ . '/sessions/*.json'); + } - return $cachedTopology; + /** @dataProvider provideTransactionsTests */ + public function testTransactions(UnifiedTestCase $test): void + { + self::$runner->run($test); } - /** - * Checks is a test format schema version is supported. - * - * @param string $schemaVersion - * @return boolean - */ - private function isSchemaVersionSupported($schemaVersion) + public function provideTransactionsTests() { - return version_compare($schemaVersion, self::MIN_SCHEMA_VERSION, '>=') && version_compare($schemaVersion, self::MAX_SCHEMA_VERSION, '<'); + return $this->provideTests(__DIR__ . '/transactions/*.json'); } /** - * Kill all sessions on the cluster. - * - * This will clean up any open transactions that may remain from a - * previously failed test. For sharded clusters, this command will be run - * on all mongos nodes. + * @dataProvider provideVersionedApiTests + * @group versioned-api */ - private static function killAllSessions() + public function testVersionedApi(UnifiedTestCase $test): void { - $manager = self::$internalClient->getManager(); - $primary = $manager->selectServer(new ReadPreference(ReadPreference::PRIMARY)); - $servers = $primary->getType() === Server::TYPE_MONGOS ? $manager->getServers() : [$primary]; - - foreach ($servers as $server) { - try { - // Skip servers that do not support sessions - if (! isset($server->getInfo()['logicalSessionTimeoutMinutes'])) { - continue; - } - - $command = new DatabaseCommand('admin', ['killAllSessions' => []]); - $command->execute($server); - } catch (ServerException $e) { - // Interrupted error is safe to ignore (see: SERVER-38335) - if ($e->getCode() != self::SERVER_ERROR_INTERRUPTED) { - throw $e; - } - } - } + self::$runner->run($test); } - private function assertOutcome(array $outcome) + public function provideVersionedApiTests() { - $this->assertNotEmpty($outcome); - $this->assertContainsOnly('object', $outcome); - - foreach ($outcome as $data) { - $collectionData = new CollectionData($data); - $collectionData->assertOutcome(self::$internalClient); - } + return $this->provideTests(__DIR__ . '/versioned-api/*.json'); } - private function prepareInitialData(array $initialData) + /** @dataProvider providePassingTests */ + public function testPassingTests(UnifiedTestCase $test): void { - $this->assertNotEmpty($initialData); - $this->assertContainsOnly('object', $initialData); + self::$runner->run($test); + } - foreach ($initialData as $data) { - $collectionData = new CollectionData($data); - $collectionData->prepareInitialData(self::$internalClient); - } + public function providePassingTests() + { + yield from $this->provideTests(__DIR__ . '/valid-pass/*.json'); } - /** - * Work around potential error executing distinct on sharded clusters. - * - * @see https://github.com/mongodb/specifications/tree/master/source/transactions/tests#why-do-tests-that-run-distinct-sometimes-fail-with-staledbversionts. - */ - private function preventStaleDbVersionError(array $operations, Context $context) + /** @dataProvider provideFailingTests */ + public function testFailingTests(UnifiedTestCase $test): void { - if (! $this->isShardedCluster()) { - return; - } + // Cannot use expectException(), as it ignores PHPUnit Exceptions + $failed = false; - $hasStartTransaction = false; - $hasDistinct = false; - $collection = null; + // phpcs:disable SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException + /* Failing tests should never produce PHP errors, so intentionally catch + * Exception instead of Throwable. */ + try { + self::$runner->run($test); + } catch (Exception $e) { + // Respect skipped tests (e.g. evaluated runOnRequirements) + if ($e instanceof SkippedTest) { + throw $e; + } - foreach ($operations as $operation) { - switch ($operation->name) { - case 'distinct': - $hasDistinct = true; - $collection = $context->getEntityMap()[$operation->object]; - break; + /* As is done in PHPUnit\Framework\TestCase::runBare(), exceptions + * other than a select few will indicate a test failure. We cannot + * call TestCase::hasFailed() because runBare() has yet to catch the + * exceptions and update the TestCase's status. + * + * IncompleteTest is intentionally omitted as it is thrown for an + * incompatible schema. This differs from PHPUnit's internal logic. + */ + $failed = ! ($e instanceof Warning); + } - case 'startTransaction': - $hasStartTransaction = true; - break; + // phpcs:enable - default: - continue 2; - } + $this->assertTrue($failed, 'Expected test to throw an exception'); + } + + public function provideFailingTests() + { + yield from $this->provideTests(__DIR__ . '/valid-fail/*.json'); + } - if ($hasStartTransaction && $hasDistinct) { - $this->assertInstanceOf(Collection::class, $collection); - $collection->distinct('foo'); + private function provideTests(string $pattern): Generator + { + foreach (glob($pattern) as $filename) { + $group = basename(dirname($filename)); - return; + foreach (UnifiedTestCase::fromFile($filename) as $name => $test) { + yield $group . '/' . $name => [$test]; } } } diff --git a/tests/UnifiedSpecTests/UnifiedTestCase.php b/tests/UnifiedSpecTests/UnifiedTestCase.php new file mode 100644 index 000000000..ced1adec0 --- /dev/null +++ b/tests/UnifiedSpecTests/UnifiedTestCase.php @@ -0,0 +1,112 @@ +test = $test; + $this->schemaVersion = $schemaVersion; + $this->runOnRequirements = $runOnRequirements; + $this->createEntities = $createEntities; + $this->initialData = $initialData; + } + + /** + * Return this object as arguments for UnifiedTestRunner::doTestCase(). + * + * This allows the UnifiedTest object to be used directly with the argument + * unpacking operator (i.e. "..."). + * + * @see https://php.net/manual/en/iteratoraggregate.getiterator.php + * @see https://php.net/manual/en/functions.arguments.php#functions.variable-arg-list + */ + public function getIterator(): Traversable + { + yield $this->test; + yield $this->schemaVersion; + yield $this->runOnRequirements; + yield $this->createEntities; + yield $this->initialData; + } + + /** + * Yields UnifiedTestCase objects for a JSON file. + */ + public static function fromFile(string $filename): Generator + { + /* Decode the file through the driver's extended JSON parser to ensure + * proper handling of special types. */ + $json = toPHP(fromJSON(file_get_contents($filename))); + + yield from static::fromJSON($json); + } + + /** + * Yields UnifiedTestCase objects for parsed JSON. + * + * The top-level and test-level "description" fields will be concatenated + * and used as the key for each yielded value. + */ + public static function fromJSON(stdClass $json): Generator + { + $description = $json->description; + $schemaVersion = $json->schemaVersion; + $runOnRequirements = $json->runOnRequirements ?? null; + $createEntities = $json->createEntities ?? null; + $initialData = $json->initialData ?? null; + $tests = $json->tests; + + /* Assertions in data providers do not count towards test assertions + * but failures will interrupt the test suite with a warning. */ + assertIsString($description); + assertIsString($schemaVersion); + assertIsArray($tests); + + foreach ($tests as $test) { + assertIsObject($test); + assertIsString($test->description); + + $name = $description . ': ' . $test->description; + + yield $name => new self($test, $schemaVersion, $runOnRequirements, $createEntities, $initialData); + } + } +} diff --git a/tests/UnifiedSpecTests/UnifiedTestRunner.php b/tests/UnifiedSpecTests/UnifiedTestRunner.php new file mode 100644 index 000000000..946ff32b2 --- /dev/null +++ b/tests/UnifiedSpecTests/UnifiedTestRunner.php @@ -0,0 +1,587 @@ +internalClient = FunctionalTestCase::createTestClient($internalClientUri); + $this->internalClientUri = $internalClientUri; + + /* Atlas prohibits killAllSessions. Inspect the connection string to + * determine if we should avoid calling killAllSessions(). This does + * mean that lingering transactions could block test execution. */ + if ($this->isServerless() || strpos($internalClientUri, self::ATLAS_TLD) !== false) { + $this->allowKillAllSessions = false; + } + + $this->serverParameterHelper = new ServerParameterHelper($this->internalClient); + } + + public function run(UnifiedTestCase $test): void + { + $this->doSetUp(); + $hasFailed = false; + + try { + $this->doTestCase(...$test); + } catch (Throwable $e) { + /* As is done in PHPUnit\Framework\TestCase::runBare(), exceptions + * other than a select few will indicate a test failure. We cannot + * call TestCase::hasFailed() for two reasons: runBare() has yet to + * catch the exceptions and update the TestCase's status and, more + * importantly, this class does not have access to the TestCase. */ + $hasFailed = ! ($e instanceof IncompleteTest || $e instanceof SkippedTest || $e instanceof Warning); + + throw $e; + } finally { + /* An EntityMap observer should be invoked irrespective of the test + * succeeding or failing. Since the callable itself might throw, we + * need to ensure doTearDown() will still be called. */ + try { + if (isset($this->entityMapObserver, $this->entityMap)) { + call_user_func($this->entityMapObserver, $this->entityMap); + } + } finally { + $this->doTearDown($hasFailed); + } + } + } + + /** + * Defines a callable to receive the EntityMap after each test. + * + * This function is primarily used by the Atlas testing workload executor. + * + * @param callable(EntityMap):void $entityMapObserver + */ + public function setEntityMapObserver(callable $entityMapObserver): void + { + $this->entityMapObserver = $entityMapObserver; + } + + private function doSetUp(): void + { + /* The transactions spec advises calling killAllSessions only at the + * start of the test suite and after failed tests; however, the "unpin + * after transient error within a transaction" pinning test causes the + * subsequent transaction test to block. */ + $this->killAllSessions(); + + $this->failPointObserver = new FailPointObserver(); + $this->failPointObserver->start(); + } + + private function doTearDown(bool $hasFailed): void + { + $this->entityMap = null; + + if ($hasFailed) { + $this->killAllSessions(); + } + + $this->failPointObserver->stop(); + $this->failPointObserver->disableFailPoints(); + + /* Manually invoking garbage collection since each test is prone to + * create cycles (perhaps due to EntityMap), which can leak and prevent + * sessions from being released back into the pool. */ + gc_collect_cycles(); + } + + private function doTestCase(stdClass $test, string $schemaVersion, ?array $runOnRequirements = null, ?array $createEntities = null, ?array $initialData = null): void + { + if (! $this->isSchemaVersionSupported($schemaVersion)) { + Assert::markTestIncomplete(sprintf('Test format schema version "%s" is not supported', $schemaVersion)); + } + + if (isset($runOnRequirements)) { + $this->checkRunOnRequirements($runOnRequirements); + } + + if (isset($test->skipReason)) { + assertIsString($test->skipReason); + Assert::markTestSkipped($test->skipReason); + } + + if (isset($test->runOnRequirements)) { + assertIsArray($test->runOnRequirements); + $this->checkRunOnRequirements($test->runOnRequirements); + } + + if (isset($initialData)) { + $this->prepareInitialData($initialData); + } + + $context = $this->createContext(); + + /* If an EntityMap observer has been configured, assign the Context's + * EntityMap to a class property so it can later be accessed from run(), + * irrespective of whether this test succeeds or fails. */ + if (isset($this->entityMapObserver)) { + $this->entityMap = $context->getEntityMap(); + } + + if (isset($createEntities)) { + $context->createEntities($createEntities); + } + + assertIsArray($test->operations); + $this->preventStaleDbVersionError($test->operations, $context); + + $context->startEventObservers(); + $context->startEventCollectors(); + + foreach ($test->operations as $o) { + $operation = new Operation($o, $context); + $operation->assert(); + } + + $context->stopEventObservers(); + $context->stopEventCollectors(); + + if (isset($test->expectEvents)) { + assertIsArray($test->expectEvents); + $context->assertExpectedEventsForClients($test->expectEvents); + } + + if (isset($test->outcome)) { + assertIsArray($test->outcome); + $this->assertOutcome($test->outcome); + } + } + + /** + * Checks server version and topology requirements. + * + * Arguments for RunOnRequirement::isSatisfied() will be cached internally. + * + * @throws SkippedTest unless one or more runOnRequirements are met + */ + private function checkRunOnRequirements(array $runOnRequirements): void + { + static $cachedIsSatisfiedArgs; + + assertNotEmpty($runOnRequirements); + assertContainsOnly('object', $runOnRequirements); + + if (! isset($cachedIsSatisfiedArgs)) { + $cachedIsSatisfiedArgs = [ + $this->getServerVersion(), + $this->getTopology(), + $this->serverParameterHelper, + $this->isAuthenticated(), + $this->isServerless(), + $this->isClientSideEncryptionSupported(), + ]; + } + + foreach ($runOnRequirements as $o) { + $runOnRequirement = new RunOnRequirement($o); + if ($runOnRequirement->isSatisfied(...$cachedIsSatisfiedArgs)) { + return; + } + } + + // @todo Add server parameter requirements? + Assert::markTestSkipped(sprintf( + 'Server (version=%s, topology=%s, auth=%s) does not meet test requirements', + $cachedIsSatisfiedArgs[0], + $cachedIsSatisfiedArgs[1], + $cachedIsSatisfiedArgs[3] ? 'yes' : 'no' + )); + } + + private function getPrimaryServer(): Server + { + $manager = $this->internalClient->getManager(); + + return $manager->selectServer(); + } + + private function getServerVersion(): string + { + $database = $this->internalClient->selectDatabase('admin'); + $buildInfo = $database->command(['buildInfo' => 1])->toArray()[0]; + + if (isset($buildInfo->version) && is_string($buildInfo->version)) { + return preg_replace('#^(\d+\.\d+\.\d+).*$#', '\1', $buildInfo->version); + } + + throw new UnexpectedValueException('Could not determine server version'); + } + + /** + * Return the topology type. + * + * @throws UnexpectedValueException if topology is neither single nor RS nor sharded + */ + private function getTopology(): string + { + switch ($this->getPrimaryServer()->getType()) { + case Server::TYPE_STANDALONE: + return RunOnRequirement::TOPOLOGY_SINGLE; + + case Server::TYPE_RS_PRIMARY: + return RunOnRequirement::TOPOLOGY_REPLICASET; + + case Server::TYPE_MONGOS: + return $this->isShardedClusterUsingReplicasets() + ? RunOnRequirement::TOPOLOGY_SHARDED_REPLICASET + : RunOnRequirement::TOPOLOGY_SHARDED; + + case Server::TYPE_LOAD_BALANCER: + return RunOnRequirement::TOPOLOGY_LOAD_BALANCED; + + default: + throw new UnexpectedValueException('Topology is neither single nor RS nor sharded'); + } + } + + /** + * Return whether the connection is authenticated. + * + * Note: if the connectionStatus command is not portable for serverless, it + * may be necessary to rewrite this to instead inspect the connection string + * or consult an environment variable, as is done in libmongoc. + */ + private function isAuthenticated(): bool + { + $database = $this->internalClient->selectDatabase('admin'); + $connectionStatus = $database->command(['connectionStatus' => 1])->toArray()[0]; + + if (isset($connectionStatus->authInfo->authenticatedUsers) && $connectionStatus->authInfo->authenticatedUsers instanceof BSONArray) { + return count($connectionStatus->authInfo->authenticatedUsers) > 0; + } + + throw new UnexpectedValueException('Could not determine authentication status'); + } + + /** + * Return whether client-side encryption is supported. + */ + private function isClientSideEncryptionSupported(): bool + { + /* CSFLE technically requires FCV 4.2+ but this is sufficient since we + * do not test on mixed-version clusters. */ + if (version_compare($this->getServerVersion(), '4.2', '<')) { + return false; + } + + if (FunctionalTestCase::getModuleInfo('libmongocrypt') === 'disabled') { + return false; + } + + return static::isCryptSharedLibAvailable() || static::isMongocryptdAvailable(); + } + + private static function isCryptSharedLibAvailable(): bool + { + $cryptSharedLibPath = getenv('CRYPT_SHARED_LIB_PATH'); + + if ($cryptSharedLibPath === false) { + return false; + } + + return is_readable($cryptSharedLibPath); + } + + private static function isMongocryptdAvailable(): bool + { + $paths = explode(PATH_SEPARATOR, getenv("PATH")); + + foreach ($paths as $path) { + if (is_executable($path . DIRECTORY_SEPARATOR . 'mongocryptd')) { + return true; + } + } + + return false; + } + + /** + * Return whether serverless (i.e. proxy as mongos) is being utilized. + */ + private function isServerless(): bool + { + $isServerless = getenv('MONGODB_IS_SERVERLESS'); + + return $isServerless !== false ? filter_var($isServerless, FILTER_VALIDATE_BOOLEAN) : false; + } + + /** + * Checks is a test format schema version is supported. + */ + private function isSchemaVersionSupported(string $schemaVersion): bool + { + return version_compare($schemaVersion, self::MIN_SCHEMA_VERSION, '>=') && version_compare($schemaVersion, self::MAX_SCHEMA_VERSION, '<='); + } + + private function isShardedClusterUsingReplicasets(): bool + { + $collection = $this->internalClient->selectCollection('config', 'shards'); + $config = $collection->findOne(); + + if ($config === null) { + return false; + } + + /** + * Use regular expression to distinguish between standalone or replicaset: + * Without a replicaset: "host" : "localhost:4100" + * With a replicaset: "host" : "dec6d8a7-9bc1-4c0e-960c-615f860b956f/localhost:4400,localhost:4401" + */ + return preg_match('@^.*/.*:\d+@', $config['host']); + } + + /** + * Kill all sessions on the cluster. + * + * This will clean up any open transactions that may remain from a + * previously failed test. For sharded clusters, this command will be run + * on all mongos nodes. + * + * This method is a NOP if allowKillAllSessions is false. + */ + private function killAllSessions(): void + { + static $ignoreErrorCodes = [ + self::SERVER_ERROR_INTERRUPTED, // SERVER-38335 + self::SERVER_ERROR_UNAUTHORIZED, // SERVER-54216 + ]; + + if (! $this->allowKillAllSessions) { + return; + } + + $manager = $this->internalClient->getManager(); + $primary = $manager->selectServer(); + $servers = $primary->getType() === Server::TYPE_MONGOS ? $manager->getServers() : [$primary]; + + foreach ($servers as $server) { + try { + /* Skip servers that do not support sessions instead of always + * attempting the command and ignoring CommandNotFound(59) */ + if (! isset($server->getInfo()['logicalSessionTimeoutMinutes'])) { + continue; + } + + $command = new DatabaseCommand('admin', ['killAllSessions' => []]); + $command->execute($server); + } catch (ServerException $e) { + if (! in_array($e->getCode(), $ignoreErrorCodes)) { + throw $e; + } + } + } + } + + private function assertOutcome(array $outcome): void + { + assertNotEmpty($outcome); + assertContainsOnly('object', $outcome); + + foreach ($outcome as $data) { + $collectionData = new CollectionData($data); + $collectionData->assertOutcome($this->internalClient); + } + } + + private function prepareInitialData(array $initialData): void + { + assertNotEmpty($initialData); + assertContainsOnly('object', $initialData); + + foreach ($initialData as $data) { + $collectionData = new CollectionData($data); + $collectionData->prepareInitialData($this->internalClient); + } + } + + /** + * Work around potential error executing distinct on sharded clusters. + * + * @see https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst#staledbversion-errors-on-sharded-clusters + */ + private function preventStaleDbVersionError(array $operations, Context $context): void + { + if ($this->getPrimaryServer()->getType() !== Server::TYPE_MONGOS) { + return; + } + + $hasStartTransaction = false; + $hasDistinct = false; + $collection = null; + + foreach ($operations as $operation) { + switch ($operation->name) { + case 'distinct': + $hasDistinct = true; + $collection = $context->getEntityMap()[$operation->object]; + break; + + case 'startTransaction': + $hasStartTransaction = true; + break; + + default: + continue 2; + } + + if ($hasStartTransaction && $hasDistinct) { + assertInstanceOf(Collection::class, $collection); + $collection->distinct('foo'); + + return; + } + } + } + + private function createContext(): Context + { + $context = new Context($this->internalClient, $this->internalClientUri); + + if ($this->getPrimaryServer()->getType() === Server::TYPE_MONGOS) { + // We assume the internal client URI has multiple mongos hosts + $multiMongosUri = $this->internalClientUri; + + if (strpos($multiMongosUri, 'mongodb+srv://') === 0) { + /* TODO: If an SRV URI is provided, we can consider connecting and + * checking the topology for multiple mongoses and then selecting a + * single mongos to reconstruct a single mongos URI; however, that + * may omit necessary URI options provided by TXT records. */ + $singleMongosUri = $multiMongosUri; + } else { + $singleMongosUri = self::removeMultipleHosts($multiMongosUri); + } + + $context->setUrisForUseMultipleMongoses($singleMongosUri, $multiMongosUri); + } + + if ($this->getPrimaryServer()->getType() === Server::TYPE_LOAD_BALANCER && ! $this->isServerless()) { + $singleMongosUri = getenv('MONGODB_SINGLE_MONGOS_LB_URI'); + $multiMongosUri = getenv('MONGODB_MULTI_MONGOS_LB_URI'); + + assertNotEmpty($singleMongosUri); + assertNotEmpty($multiMongosUri); + + $context->setUrisForUseMultipleMongoses($singleMongosUri, $multiMongosUri); + } + + return $context; + } + + /** + * Removes any hosts beyond the first in a URI. This function should only be + * used with a sharded cluster URI, but that is not enforced. + */ + private static function removeMultipleHosts(string $uri): string + { + $parts = parse_url($uri); + + assertIsArray($parts); + + $hosts = explode(',', $parts['host']); + + // Nothing to do if the URI already has a single mongos host + if (count($hosts) === 1) { + return $uri; + } + + // Re-append port to last host + if (isset($parts['port'])) { + $hosts[count($hosts) - 1] .= ':' . $parts['port']; + } + + $singleHost = $hosts[0]; + $multipleHosts = implode(',', $hosts); + + $pos = strpos($uri, $multipleHosts); + + assertNotFalse($pos); + + return substr_replace($uri, $singleHost, $pos, strlen($multipleHosts)); + } +} diff --git a/tests/UnifiedSpecTests/Util.php b/tests/UnifiedSpecTests/Util.php index 3e57f78ff..351d9ad0f 100644 --- a/tests/UnifiedSpecTests/Util.php +++ b/tests/UnifiedSpecTests/Util.php @@ -2,15 +2,25 @@ namespace MongoDB\Tests\UnifiedSpecTests; +use MongoDB\ChangeStream; +use MongoDB\Client; +use MongoDB\Collection; +use MongoDB\Database; +use MongoDB\Driver\ClientEncryption; +use MongoDB\Driver\Cursor; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadPreference; +use MongoDB\Driver\Session; use MongoDB\Driver\WriteConcern; +use MongoDB\GridFS\Bucket; use stdClass; + use function array_diff_key; use function array_fill_keys; use function array_key_exists; use function array_keys; use function implode; +use function PHPUnit\Framework\assertArrayHasKey; use function PHPUnit\Framework\assertContains; use function PHPUnit\Framework\assertEmpty; use function PHPUnit\Framework\assertIsArray; @@ -25,14 +35,119 @@ final class Util { - public static function assertHasOnlyKeys($arrayOrObject, array $keys) + /** + * Array to fill, which contains the schema of allowed attributes for operations. + */ + private static $args = [ + Operation::OBJECT_TEST_RUNNER => [ + 'assertCollectionExists' => ['databaseName', 'collectionName'], + 'assertCollectionNotExists' => ['databaseName', 'collectionName'], + 'assertIndexExists' => ['databaseName', 'collectionName', 'indexName'], + 'assertIndexNotExists' => ['databaseName', 'collectionName', 'indexName'], + 'assertSameLsidOnLastTwoCommands' => ['client'], + 'assertDifferentLsidOnLastTwoCommands' => ['client'], + 'assertNumberConnectionsCheckedOut' => ['connections'], + 'assertSessionDirty' => ['session'], + 'assertSessionNotDirty' => ['session'], + 'assertSessionPinned' => ['session'], + 'assertSessionTransactionState' => ['session', 'state'], + 'assertSessionUnpinned' => ['session'], + 'failPoint' => ['client', 'failPoint'], + 'targetedFailPoint' => ['session', 'failPoint'], + 'loop' => ['operations', 'storeErrorsAsEntity', 'storeFailuresAsEntity', 'storeSuccessesAsEntity', 'storeIterationsAsEntity'], + ], + Client::class => [ + 'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS', 'showExpandedEvents'], + 'listDatabaseNames' => ['authorizedDatabases', 'filter', 'maxTimeMS', 'session'], + 'listDatabases' => ['authorizedDatabases', 'filter', 'maxTimeMS', 'session'], + ], + ClientEncryption::class => [ + 'addKeyAltName' => ['id', 'keyAltName'], + 'createDataKey' => ['kmsProvider', 'opts'], + 'deleteKey' => ['id'], + 'getKey' => ['id'], + 'getKeyByAltName' => ['keyAltName'], + 'getKeys' => [], + 'removeKeyAltName' => ['id', 'keyAltName'], + 'rewrapManyDataKey' => ['filter', 'opts'], + ], + Database::class => [ + 'aggregate' => ['pipeline', 'session', 'useCursor', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'], + 'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS', 'showExpandedEvents'], + 'createCollection' => ['collection', 'session', 'autoIndexId', 'capped', 'changeStreamPreAndPostImages', 'clusteredIndex', 'collation', 'expireAfterSeconds', 'flags', 'indexOptionDefaults', 'max', 'maxTimeMS', 'pipeline', 'size', 'storageEngine', 'timeseries', 'validationAction', 'validationLevel', 'validator', 'viewOn'], + 'dropCollection' => ['collection', 'session'], + 'listCollectionNames' => ['authorizedCollections', 'filter', 'maxTimeMS', 'session'], + 'listCollections' => ['authorizedCollections', 'filter', 'maxTimeMS', 'session'], + 'modifyCollection' => ['collection', 'changeStreamPreAndPostImages', 'index', 'validator'], + // Note: commandName is not used by PHP + 'runCommand' => ['command', 'commandName', 'readPreference', 'session'], + ], + Collection::class => [ + 'aggregate' => ['pipeline', 'session', 'useCursor', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'], + 'bulkWrite' => ['let', 'requests', 'session', 'ordered', 'bypassDocumentValidation', 'comment'], + 'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'fullDocumentBeforeChange', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS', 'comment', 'showExpandedEvents'], + 'createFindCursor' => ['filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], + 'createIndex' => ['keys', 'comment', 'commitQuorum', 'maxTimeMS', 'name', 'session', 'unique'], + 'dropIndex' => ['name', 'session', 'maxTimeMS', 'comment'], + 'count' => ['filter', 'session', 'collation', 'hint', 'limit', 'maxTimeMS', 'skip', 'comment'], + 'countDocuments' => ['filter', 'session', 'limit', 'skip', 'collation', 'hint', 'maxTimeMS', 'comment'], + 'estimatedDocumentCount' => ['session', 'maxTimeMS', 'comment'], + 'deleteMany' => ['let', 'filter', 'session', 'collation', 'hint', 'comment'], + 'deleteOne' => ['let', 'filter', 'session', 'collation', 'hint', 'comment'], + 'findOneAndDelete' => ['let', 'filter', 'session', 'projection', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'maxTimeMS', 'new', 'sort', 'update', 'upsert', 'comment'], + 'distinct' => ['fieldName', 'filter', 'session', 'collation', 'maxTimeMS', 'comment'], + 'drop' => ['session', 'comment'], + 'find' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], + 'findOne' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], + 'findOneAndReplace' => ['let', 'returnDocument', 'filter', 'replacement', 'session', 'projection', 'returnDocument', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'maxTimeMS', 'new', 'remove', 'sort', 'comment'], + 'rename' => ['to', 'comment', 'dropTarget'], + 'replaceOne' => ['let', 'filter', 'replacement', 'session', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'comment'], + 'findOneAndUpdate' => ['let', 'returnDocument', 'filter', 'update', 'session', 'upsert', 'projection', 'remove', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'maxTimeMS', 'sort', 'comment'], + 'updateMany' => ['let', 'filter', 'update', 'session', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'comment'], + 'updateOne' => ['let', 'filter', 'update', 'session', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'comment'], + 'insertMany' => ['documents', 'session', 'ordered', 'bypassDocumentValidation', 'comment'], + 'insertOne' => ['document', 'session', 'bypassDocumentValidation', 'comment'], + 'listIndexes' => ['session', 'maxTimeMS', 'comment'], + 'mapReduce' => ['map', 'reduce', 'out', 'session', 'bypassDocumentValidation', 'collation', 'finalize', 'jsMode', 'limit', 'maxTimeMS', 'query', 'scope', 'sort', 'verbose', 'comment'], + ], + ChangeStream::class => [ + 'iterateUntilDocumentOrError' => [], + ], + Cursor::class => [ + 'close' => [], + 'iterateUntilDocumentOrError' => [], + ], + Session::class => [ + 'abortTransaction' => [], + 'commitTransaction' => [], + 'endSession' => [], + 'startTransaction' => ['maxCommitTimeMS', 'readConcern', 'readPreference', 'writeConcern'], + 'withTransaction' => ['callback', 'maxCommitTimeMS', 'readConcern', 'readPreference', 'writeConcern'], + ], + Bucket::class => [ + 'delete' => ['id'], + 'downloadByName' => ['filename', 'revision'], + 'download' => ['id'], + 'uploadWithId' => ['id', 'filename', 'source', 'chunkSizeBytes', 'disableMD5', 'contentType', 'metadata'], + 'upload' => ['filename', 'source', 'chunkSizeBytes', 'disableMD5', 'contentType', 'metadata'], + ], + ]; + + public static function assertHasOnlyKeys($arrayOrObject, array $keys): void { assertThat($arrayOrObject, logicalOr(isType('array'), isInstanceOf(stdClass::class))); $diff = array_diff_key((array) $arrayOrObject, array_fill_keys($keys, 1)); assertEmpty($diff, 'Unsupported keys: ' . implode(',', array_keys($diff))); } - public static function createReadConcern(stdClass $o) : ReadConcern + public static function assertArgumentsBySchema(string $executingObjectName, string $operation, array $args): void + { + assertArrayHasKey($executingObjectName, self::$args); + assertArrayHasKey($operation, self::$args[$executingObjectName]); + self::assertHasOnlyKeys($args, self::$args[$executingObjectName][$operation]); + } + + public static function createReadConcern(stdClass $o): ReadConcern { self::assertHasOnlyKeys($o, ['level']); @@ -42,7 +157,7 @@ public static function createReadConcern(stdClass $o) : ReadConcern return new ReadConcern($level); } - public static function createReadPreference(stdClass $o) : ReadPreference + public static function createReadPreference(stdClass $o): ReadPreference { self::assertHasOnlyKeys($o, ['mode', 'tagSets', 'maxStalenessSeconds', 'hedge']); @@ -73,7 +188,7 @@ public static function createReadPreference(stdClass $o) : ReadPreference return new ReadPreference($mode, $tagSets, $options); } - public static function createWriteConcern(stdClass $o) : WriteConcern + public static function createWriteConcern(stdClass $o): WriteConcern { self::assertHasOnlyKeys($o, ['w', 'wtimeoutMS', 'journal']); @@ -94,7 +209,7 @@ public static function createWriteConcern(stdClass $o) : WriteConcern return new WriteConcern(...$args); } - public static function prepareCommonOptions(array $options) : array + public static function prepareCommonOptions(array $options): array { if (array_key_exists('readConcern', $options)) { assertIsObject($options['readConcern']); diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-clusterTime.json b/tests/UnifiedSpecTests/change-streams/change-streams-clusterTime.json new file mode 100644 index 000000000..2b09e548f --- /dev/null +++ b/tests/UnifiedSpecTests/change-streams/change-streams-clusterTime.json @@ -0,0 +1,81 @@ +{ + "description": "change-streams-clusterTime", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "4.0.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [] + } + ], + "tests": [ + { + "description": "clusterTime is present", + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "ns": { + "db": "database0", + "coll": "collection0" + }, + "clusterTime": { + "$$exists": true + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-disambiguatedPaths.json b/tests/UnifiedSpecTests/change-streams/change-streams-disambiguatedPaths.json new file mode 100644 index 000000000..e6cc5ef66 --- /dev/null +++ b/tests/UnifiedSpecTests/change-streams/change-streams-disambiguatedPaths.json @@ -0,0 +1,251 @@ +{ + "description": "disambiguatedPaths", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "6.1.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [] + } + ], + "tests": [ + { + "description": "disambiguatedPaths is not present when showExpandedEvents is false/unset", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": { + "1": 1 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "a.1": 2 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "updateDescription": { + "updatedFields": { + "$$exists": true + }, + "removedFields": { + "$$exists": true + }, + "truncatedArrays": { + "$$exists": true + }, + "disambiguatedPaths": { + "$$exists": false + } + } + } + } + ] + }, + { + "description": "disambiguatedPaths is present on updateDescription when an ambiguous path is present", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": { + "1": 1 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "a.1": 2 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "updateDescription": { + "updatedFields": { + "$$exists": true + }, + "removedFields": { + "$$exists": true + }, + "truncatedArrays": { + "$$exists": true + }, + "disambiguatedPaths": { + "a.1": [ + "a", + "1" + ] + } + } + } + } + ] + }, + { + "description": "disambiguatedPaths returns array indices as integers", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": [ + { + "1": 1 + } + ] + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "a.0.1": 2 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "updateDescription": { + "updatedFields": { + "$$exists": true + }, + "removedFields": { + "$$exists": true + }, + "truncatedArrays": { + "$$exists": true + }, + "disambiguatedPaths": { + "a.0.1": [ + "a", + { + "$$type": "int" + }, + "1" + ] + } + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-errors.json b/tests/UnifiedSpecTests/change-streams/change-streams-errors.json new file mode 100644 index 000000000..65e99e541 --- /dev/null +++ b/tests/UnifiedSpecTests/change-streams/change-streams-errors.json @@ -0,0 +1,246 @@ +{ + "description": "change-streams-errors", + "schemaVersion": "1.7", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ], + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "globalClient", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + }, + { + "database": { + "id": "globalDatabase0", + "client": "globalClient", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "globalCollection0", + "database": "globalDatabase0", + "collectionName": "collection0" + } + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [] + } + ], + "tests": [ + { + "description": "The watch helper must not throw a custom exception when executed against a single server topology, but instead depend on a server error", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "topologies": [ + "single" + ] + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "expectError": { + "errorCode": 40573 + } + } + ] + }, + { + "description": "Change Stream should error when an invalid aggregation stage is passed in", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$unsupported": "foo" + } + ] + }, + "expectError": { + "errorCode": 40324 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + }, + { + "$unsupported": "foo" + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Change Stream should error when _id is projected out", + "runOnRequirements": [ + { + "minServerVersion": "4.1.11", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 0 + } + } + ] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "z": 3 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectError": { + "errorCode": 280 + } + } + ] + }, + { + "description": "change stream errors on ElectionInProgress", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 216, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "z": 3 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectError": { + "errorCode": 216 + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-pre_and_post_images.json b/tests/UnifiedSpecTests/change-streams/change-streams-pre_and_post_images.json new file mode 100644 index 000000000..e62fc0345 --- /dev/null +++ b/tests/UnifiedSpecTests/change-streams/change-streams-pre_and_post_images.json @@ -0,0 +1,827 @@ +{ + "description": "change-streams-pre_and_post_images", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "6.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "collMod", + "insert", + "update", + "getMore", + "killCursors" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "change-stream-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "change-stream-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ], + "tests": [ + { + "description": "fullDocument:whenAvailable with changeStreamPreAndPostImages enabled", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "test", + "changeStreamPreAndPostImages": { + "enabled": true + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "fullDocument": "whenAvailable" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "updateDescription": { + "$$type": "object" + }, + "fullDocument": { + "_id": 1, + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$changeStream": { + "fullDocument": "whenAvailable" + } + } + ] + } + } + } + ] + } + ] + }, + { + "description": "fullDocument:whenAvailable with changeStreamPreAndPostImages disabled", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "test", + "changeStreamPreAndPostImages": { + "enabled": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "fullDocument": "whenAvailable" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "updateDescription": { + "$$type": "object" + }, + "fullDocument": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$changeStream": { + "fullDocument": "whenAvailable" + } + } + ] + } + } + } + ] + } + ] + }, + { + "description": "fullDocument:required with changeStreamPreAndPostImages enabled", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "test", + "changeStreamPreAndPostImages": { + "enabled": true + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "fullDocument": "required" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "updateDescription": { + "$$type": "object" + }, + "fullDocument": { + "_id": 1, + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$changeStream": { + "fullDocument": "required" + } + } + ] + } + } + } + ] + } + ] + }, + { + "description": "fullDocument:required with changeStreamPreAndPostImages disabled", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "test", + "changeStreamPreAndPostImages": { + "enabled": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "fullDocument": "required" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$changeStream": { + "fullDocument": "required" + } + } + ] + } + } + } + ] + } + ] + }, + { + "description": "fullDocumentBeforeChange:whenAvailable with changeStreamPreAndPostImages enabled", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "test", + "changeStreamPreAndPostImages": { + "enabled": true + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "fullDocumentBeforeChange": "whenAvailable" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "updateDescription": { + "$$type": "object" + }, + "fullDocumentBeforeChange": { + "_id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$changeStream": { + "fullDocumentBeforeChange": "whenAvailable" + } + } + ] + } + } + } + ] + } + ] + }, + { + "description": "fullDocumentBeforeChange:whenAvailable with changeStreamPreAndPostImages disabled", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "test", + "changeStreamPreAndPostImages": { + "enabled": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "fullDocumentBeforeChange": "whenAvailable" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "updateDescription": { + "$$type": "object" + }, + "fullDocumentBeforeChange": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$changeStream": { + "fullDocumentBeforeChange": "whenAvailable" + } + } + ] + } + } + } + ] + } + ] + }, + { + "description": "fullDocumentBeforeChange:required with changeStreamPreAndPostImages enabled", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "test", + "changeStreamPreAndPostImages": { + "enabled": true + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "fullDocumentBeforeChange": "required" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "updateDescription": { + "$$type": "object" + }, + "fullDocumentBeforeChange": { + "_id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$changeStream": { + "fullDocumentBeforeChange": "required" + } + } + ] + } + } + } + ] + } + ] + }, + { + "description": "fullDocumentBeforeChange:required with changeStreamPreAndPostImages disabled", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "test", + "changeStreamPreAndPostImages": { + "enabled": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "fullDocumentBeforeChange": "required" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$changeStream": { + "fullDocumentBeforeChange": "required" + } + } + ] + } + } + } + ] + } + ] + }, + { + "description": "fullDocumentBeforeChange:off with changeStreamPreAndPostImages enabled", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "test", + "changeStreamPreAndPostImages": { + "enabled": true + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "fullDocumentBeforeChange": "off" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "updateDescription": { + "$$type": "object" + }, + "fullDocumentBeforeChange": { + "$$exists": false + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$changeStream": { + "fullDocumentBeforeChange": "off" + } + } + ] + } + } + } + ] + } + ] + }, + { + "description": "fullDocumentBeforeChange:off with changeStreamPreAndPostImages disabled", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "test", + "changeStreamPreAndPostImages": { + "enabled": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "fullDocumentBeforeChange": "off" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "updateDescription": { + "$$type": "object" + }, + "fullDocumentBeforeChange": { + "$$exists": false + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$changeStream": { + "fullDocumentBeforeChange": "off" + } + } + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-resume-allowlist.json b/tests/UnifiedSpecTests/change-streams/change-streams-resume-allowlist.json new file mode 100644 index 000000000..1ec72b432 --- /dev/null +++ b/tests/UnifiedSpecTests/change-streams/change-streams-resume-allowlist.json @@ -0,0 +1,2348 @@ +{ + "description": "change-streams-resume-allowlist", + "schemaVersion": "1.7", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ], + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "globalClient", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + }, + { + "database": { + "id": "globalDatabase0", + "client": "globalClient", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "globalCollection0", + "database": "globalDatabase0", + "collectionName": "collection0" + } + } + ], + "tests": [ + { + "description": "change stream resumes after a network error", + "runOnRequirements": [ + { + "minServerVersion": "4.2" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "closeConnection": true + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after HostUnreachable", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 6, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after HostNotFound", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 7, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after NetworkTimeout", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 89, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after ShutdownInProgress", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 91, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after PrimarySteppedDown", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 189, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after ExceededTimeLimit", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 262, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after SocketException", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 9001, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after NotWritablePrimary", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 10107, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after InterruptedAtShutdown", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 11600, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after InterruptedDueToReplStateChange", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 11602, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after NotPrimaryNoSecondaryOk", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 13435, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after NotPrimaryOrSecondary", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 13436, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after StaleShardVersion", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 63, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after StaleEpoch", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 150, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after RetryChangeStream", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 234, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after FailedToSatisfyReadPreference", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 133, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after CursorNotFound", + "runOnRequirements": [ + { + "minServerVersion": "4.2" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 43, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-resume-errorLabels.json b/tests/UnifiedSpecTests/change-streams/change-streams-resume-errorLabels.json new file mode 100644 index 000000000..7fd70108f --- /dev/null +++ b/tests/UnifiedSpecTests/change-streams/change-streams-resume-errorLabels.json @@ -0,0 +1,2130 @@ +{ + "description": "change-streams-resume-errorlabels", + "schemaVersion": "1.7", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ], + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "globalClient", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + }, + { + "database": { + "id": "globalDatabase0", + "client": "globalClient", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "globalCollection0", + "database": "globalDatabase0", + "collectionName": "collection0" + } + } + ], + "tests": [ + { + "description": "change stream resumes after HostUnreachable", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 6, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after HostNotFound", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 7, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after NetworkTimeout", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 89, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 91, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 189, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after ExceededTimeLimit", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 262, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after SocketException", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 9001, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after NotWritablePrimary", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 10107, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after InterruptedAtShutdown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 11600, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after InterruptedDueToReplStateChange", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 11602, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after NotPrimaryNoSecondaryOk", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 13435, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after NotPrimaryOrSecondary", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 13436, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after StaleShardVersion", + "runOnRequirements": [ + { + "maxServerVersion": "6.0.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 63, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after StaleEpoch", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 150, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after RetryChangeStream", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 234, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes after FailedToSatisfyReadPreference", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failGetMoreAfterCursorCheckout", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 133, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream resumes if error contains ResumableChangeStreamError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 50, + "closeConnection": false, + "errorLabels": [ + "ResumableChangeStreamError" + ] + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$exists": true + }, + "collection": "collection0" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "resumeAfter": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "change stream does not resume if error does not contain ResumableChangeStreamError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 6, + "closeConnection": false + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectError": { + "errorCode": 6 + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-showExpandedEvents.json b/tests/UnifiedSpecTests/change-streams/change-streams-showExpandedEvents.json new file mode 100644 index 000000000..b9594e0c1 --- /dev/null +++ b/tests/UnifiedSpecTests/change-streams/change-streams-showExpandedEvents.json @@ -0,0 +1,516 @@ +{ + "description": "change-streams-showExpandedEvents", + "schemaVersion": "1.7", + "runOnRequirements": [ + { + "minServerVersion": "6.0.0", + "topologies": [ + "replicaset", + "sharded" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ], + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + }, + { + "database": { + "id": "database1", + "client": "client0", + "databaseName": "database1" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "collection1" + } + }, + { + "database": { + "id": "shardedDb", + "client": "client0", + "databaseName": "shardedDb" + } + }, + { + "database": { + "id": "adminDb", + "client": "client0", + "databaseName": "admin" + } + }, + { + "collection": { + "id": "shardedCollection", + "database": "shardedDb", + "collectionName": "shardedCollection" + } + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [] + } + ], + "tests": [ + { + "description": "when provided, showExpandedEvents is sent as a part of the aggregate command", + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "showExpandedEvents": true + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "when omitted, showExpandedEvents is not sent as a part of the aggregate command", + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "showExpandedEvents": { + "$$exists": false + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "when showExpandedEvents is true, new fields on change stream events are handled appropriately", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "foo" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "foo" + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "a": 1 + } + } + }, + { + "name": "createIndex", + "object": "collection0", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "rename", + "object": "collection0", + "arguments": { + "to": "foo", + "dropTarget": true + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "collectionUUID": { + "$$exists": true + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "createIndexes", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "operationDescription": { + "$$exists": true + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "rename", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "to": { + "db": "database0", + "coll": "foo" + }, + "operationDescription": { + "dropTarget": { + "$$exists": true + }, + "to": { + "db": "database0", + "coll": "foo" + } + } + } + } + ] + }, + { + "description": "when showExpandedEvents is true, createIndex events are reported", + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "operationType": { + "$ne": "create" + } + } + } + ], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "createIndex", + "object": "collection0", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "createIndexes" + } + } + ] + }, + { + "description": "when showExpandedEvents is true, dropIndexes events are reported", + "operations": [ + { + "name": "createIndex", + "object": "collection0", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "dropIndex", + "object": "collection0", + "arguments": { + "name": "x_1" + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "dropIndexes" + } + } + ] + }, + { + "description": "when showExpandedEvents is true, create events are reported", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "foo" + } + }, + { + "name": "createChangeStream", + "object": "database0", + "arguments": { + "pipeline": [], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "foo" + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "create" + } + } + ] + }, + { + "description": "when showExpandedEvents is true, create events on views are reported", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "foo" + } + }, + { + "name": "createChangeStream", + "object": "database0", + "arguments": { + "pipeline": [], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "foo", + "viewOn": "testName" + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "create" + } + } + ] + }, + { + "description": "when showExpandedEvents is true, modify events are reported", + "operations": [ + { + "name": "createIndex", + "object": "collection0", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_2" + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "runCommand", + "object": "database0", + "arguments": { + "command": { + "collMod": "collection0" + }, + "commandName": "collMod" + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "modify" + } + } + ] + }, + { + "description": "when showExpandedEvents is true, shardCollection events are reported", + "runOnRequirements": [ + { + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "dropCollection", + "object": "shardedDb", + "arguments": { + "collection": "shardedCollection" + } + }, + { + "name": "createCollection", + "object": "shardedDb", + "arguments": { + "collection": "shardedCollection" + } + }, + { + "name": "createChangeStream", + "object": "shardedCollection", + "arguments": { + "pipeline": [], + "showExpandedEvents": true + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "runCommand", + "object": "adminDb", + "arguments": { + "command": { + "shardCollection": "shardedDb.shardedCollection", + "key": { + "_id": 1 + } + }, + "commandName": "shardCollection" + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "shardCollection" + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams.json b/tests/UnifiedSpecTests/change-streams/change-streams.json new file mode 100644 index 000000000..c8b60ed4e --- /dev/null +++ b/tests/UnifiedSpecTests/change-streams/change-streams.json @@ -0,0 +1,1795 @@ +{ + "description": "change-streams", + "schemaVersion": "1.7", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ], + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "globalClient", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + }, + { + "database": { + "id": "database1", + "client": "client0", + "databaseName": "database1" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "collection1" + } + }, + { + "database": { + "id": "globalDatabase0", + "client": "globalClient", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "globalCollection0", + "database": "globalDatabase0", + "collectionName": "collection0" + } + }, + { + "database": { + "id": "globalDatabase1", + "client": "globalClient", + "databaseName": "database1" + } + }, + { + "collection": { + "id": "globalCollection1", + "database": "globalDatabase1", + "collectionName": "collection1" + } + }, + { + "collection": { + "id": "globalDb1Collection0", + "database": "globalDatabase1", + "collectionName": "collection0" + } + }, + { + "collection": { + "id": "globalDb0Collection1", + "database": "globalDatabase0", + "collectionName": "collection1" + } + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [] + } + ], + "tests": [ + { + "description": "Test array truncation", + "runOnRequirements": [ + { + "minServerVersion": "4.7" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1, + "array": [ + "foo", + { + "a": "bar" + }, + 1, + 2, + 3 + ] + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "array": [ + "foo", + { + "a": "bar" + } + ] + } + } + ] + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "updateDescription": { + "updatedFields": {}, + "removedFields": [], + "truncatedArrays": [ + { + "field": "array", + "newSize": 2 + } + ] + } + } + } + ] + }, + { + "description": "Test with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "comment": { + "name": "test1" + } + }, + "saveResultAsEntity": "changeStream0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$changeStream": {} + } + ], + "comment": { + "name": "test1" + } + } + } + } + ] + } + ] + }, + { + "description": "Test with document comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "comment": { + "name": "test1" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$changeStream": {} + } + ], + "comment": { + "name": "test1" + } + } + } + } + ] + } + ] + }, + { + "description": "Test with string comment", + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "comment": "comment" + }, + "saveResultAsEntity": "changeStream0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$changeStream": {} + } + ], + "comment": "comment" + } + } + } + ] + } + ] + }, + { + "description": "Test that comment is set on getMore", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "comment": { + "key": "value" + } + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$changeStream": {} + } + ], + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "collection0", + "documents": [ + { + "_id": 1, + "a": 1 + } + ] + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection0", + "comment": { + "key": "value" + } + }, + "commandName": "getMore", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Test that comment is not set on getMore - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.3.99" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "comment": "comment" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$changeStream": {} + } + ], + "comment": "comment" + } + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "collection0", + "documents": [ + { + "_id": 1, + "a": 1 + } + ] + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection0", + "comment": { + "$$exists": false + } + }, + "commandName": "getMore", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "to field is set in a rename change event", + "runOnRequirements": [ + { + "minServerVersion": "4.0.1" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "collection1" + } + }, + { + "name": "rename", + "object": "collection0", + "arguments": { + "to": "collection1" + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "rename", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "to": { + "db": "database0", + "coll": "collection1" + } + } + } + ] + }, + { + "description": "Test unknown operationType MUST NOT err", + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "operationType": "addedInFutureMongoDBVersion", + "ns": 1 + } + } + ] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "addedInFutureMongoDBVersion", + "ns": { + "db": "database0", + "coll": "collection0" + } + } + } + ] + }, + { + "description": "Test newField added in response MUST NOT err", + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "operationType": 1, + "ns": 1, + "newField": "newFieldValue" + } + } + ] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "newField": "newFieldValue" + } + } + ] + }, + { + "description": "Test new structure in ns document MUST NOT err", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "maxServerVersion": "5.2.99" + }, + { + "minServerVersion": "6.0" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "operationType": "insert", + "ns.viewOn": "db.coll" + } + } + ] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "viewOn": "db.coll" + } + } + } + ] + }, + { + "description": "Test modified structure in ns document MUST NOT err", + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "operationType": "insert", + "ns": { + "db": "$ns.db", + "coll": "$ns.coll", + "viewOn": "db.coll" + } + } + } + ] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0", + "viewOn": "db.coll" + } + } + } + ] + }, + { + "description": "Test server error on projecting out _id", + "runOnRequirements": [ + { + "minServerVersion": "4.2" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 0 + } + } + ] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectError": { + "errorCode": 280, + "errorCodeName": "ChangeStreamFatalError", + "errorLabelsContain": [ + "NonResumableChangeStreamError" + ] + } + } + ] + }, + { + "description": "Test projection in change stream returns expected fields", + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "optype": "$operationType", + "ns": 1, + "newField": "value" + } + } + ] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "optype": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "newField": "value" + } + } + ] + }, + { + "description": "$changeStream must be the first stage in a change stream pipeline sent to the server", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "The server returns change stream responses in the specified server response format", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "_id": { + "$$exists": true + }, + "documentKey": { + "$$exists": true + }, + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + } + ] + }, + { + "description": "Executing a watch helper on a Collection results in notifications for changes to the specified collection", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalDb0Collection1", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "insertOne", + "object": "globalDb1Collection0", + "arguments": { + "document": { + "y": 2 + } + } + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "z": 3 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "z": 3, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Change Stream should allow valid aggregate pipeline stages", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "fullDocument.z": 3 + } + } + ] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "y": 2 + } + } + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "z": 3 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "z": 3, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + }, + { + "$match": { + "fullDocument.z": 3 + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Executing a watch helper on a Database results in notifications for changes to all collections in the specified database.", + "runOnRequirements": [ + { + "minServerVersion": "3.8.0" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "database0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalDb0Collection1", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "insertOne", + "object": "globalDb1Collection0", + "arguments": { + "document": { + "y": 2 + } + } + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "z": 3 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection1" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "z": 3, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Executing a watch helper on a MongoClient results in notifications for changes to all collections in all databases in the cluster.", + "runOnRequirements": [ + { + "minServerVersion": "3.8.0" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "client0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalDb0Collection1", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "insertOne", + "object": "globalDb1Collection0", + "arguments": { + "document": { + "y": 2 + } + } + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "z": 3 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection1" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database1", + "coll": "collection0" + }, + "fullDocument": { + "y": 2, + "_id": { + "$$exists": true + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "z": 3, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "Test insert, update, replace, and delete event types", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "updateOne", + "object": "globalCollection0", + "arguments": { + "filter": { + "x": 1 + }, + "update": { + "$set": { + "x": 2 + } + } + } + }, + { + "name": "replaceOne", + "object": "globalCollection0", + "arguments": { + "filter": { + "x": 2 + }, + "replacement": { + "x": 3 + } + } + }, + { + "name": "deleteOne", + "object": "globalCollection0", + "arguments": { + "filter": { + "x": 3 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "update", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "updateDescription": { + "updatedFields": { + "x": 2 + }, + "removedFields": [], + "truncatedArrays": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "replace", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 3, + "_id": { + "$$exists": true + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "delete", + "ns": { + "db": "database0", + "coll": "collection0" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Test rename and invalidate event types", + "runOnRequirements": [ + { + "minServerVersion": "4.0.1" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "collection1" + } + }, + { + "name": "rename", + "object": "globalCollection0", + "arguments": { + "to": "collection1" + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "rename", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "to": { + "db": "database0", + "coll": "collection1" + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "invalidate" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Test drop and invalidate event types", + "runOnRequirements": [ + { + "minServerVersion": "4.0.1" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "collection0" + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "drop", + "ns": { + "db": "database0", + "coll": "collection0" + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "invalidate" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Test consecutive resume", + "runOnRequirements": [ + { + "minServerVersion": "4.1.7" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "globalClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "getMore" + ], + "closeConnection": true + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "batchSize": 1 + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 2 + } + } + }, + { + "name": "insertOne", + "object": "globalCollection0", + "arguments": { + "document": { + "x": 3 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 1, + "_id": { + "$$exists": true + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 2, + "_id": { + "$$exists": true + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "fullDocument": { + "x": 3, + "_id": { + "$$exists": true + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": { + "batchSize": 1 + }, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "commandName": "aggregate", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Test wallTime field is set in a change event", + "runOnRequirements": [ + { + "minServerVersion": "6.0.0" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "database0", + "coll": "collection0" + }, + "wallTime": { + "$$exists": true + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/client-side-encryption/addKeyAltName.json b/tests/UnifiedSpecTests/client-side-encryption/addKeyAltName.json new file mode 100644 index 000000000..f70bc572a --- /dev/null +++ b/tests/UnifiedSpecTests/client-side-encryption/addKeyAltName.json @@ -0,0 +1,609 @@ +{ + "description": "addKeyAltName", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "add keyAltName to non-existent data key", + "operations": [ + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "AAAjYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "new_key_alt_name" + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "AAAjYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "new_key_alt_name" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + }, + { + "description": "add new keyAltName to data key with no keyAltNames", + "operations": [ + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "projection": { + "_id": 0, + "keyAltNames": 1 + } + }, + "expectResult": [ + { + "keyAltNames": [ + "local_key" + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "local_key" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "add existing keyAltName to existing data key", + "operations": [ + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "projection": { + "_id": 0, + "keyAltNames": 1 + } + }, + "expectResult": [ + { + "keyAltNames": [ + "local_key" + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "local_key" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "local_key" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "add new keyAltName to data key with keyAltNames", + "operations": [ + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "addKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "another_name" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 0, + "keyAltNames": "$keyAltNames" + } + }, + { + "$unwind": "$keyAltNames" + }, + { + "$sort": { + "keyAltNames": 1 + } + } + ] + }, + "expectResult": [ + { + "keyAltNames": "another_name" + }, + { + "keyAltNames": "local_key" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "local_key" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": { + "$addToSet": { + "keyAltNames": "another_name" + } + }, + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/client-side-encryption/createDataKey-kms_providers-invalid.json b/tests/UnifiedSpecTests/client-side-encryption/createDataKey-kms_providers-invalid.json new file mode 100644 index 000000000..2344a61a9 --- /dev/null +++ b/tests/UnifiedSpecTests/client-side-encryption/createDataKey-kms_providers-invalid.json @@ -0,0 +1,119 @@ +{ + "description": "createDataKey-kms_providers-invalid", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + } + } + } + ], + "tests": [ + { + "description": "create data key without required master key fields", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "aws", + "opts": { + "masterKey": {} + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "create data key with invalid master key field", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "masterKey": { + "invalid": 1 + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "create data key with invalid master key", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "aws", + "opts": { + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "invalid" + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/client-side-encryption/createDataKey.json b/tests/UnifiedSpecTests/client-side-encryption/createDataKey.json new file mode 100644 index 000000000..110c726f9 --- /dev/null +++ b/tests/UnifiedSpecTests/client-side-encryption/createDataKey.json @@ -0,0 +1,711 @@ +{ + "description": "createDataKey", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [] + } + ], + "tests": [ + { + "description": "create data key with AWS KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "aws", + "opts": { + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with Azure KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "azure", + "opts": { + "masterKey": { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with GCP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "gcp", + "opts": { + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with KMIP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "kmip" + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with local KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local" + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "local" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with no keyAltName", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyAltNames": [] + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyAltNames": { + "$$exists": false + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with single keyAltName", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyAltNames": [ + "local_key" + ] + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with multiple keyAltNames", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyAltNames": [ + "abc", + "def" + ] + } + }, + "expectResult": { + "$$type": "binData" + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 0, + "keyAltNames": 1 + } + }, + { + "$unwind": "$keyAltNames" + }, + { + "$sort": { + "keyAltNames": 1 + } + } + ] + }, + "expectResult": [ + { + "keyAltNames": "abc" + }, + { + "keyAltNames": "def" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyAltNames": { + "$$type": "array" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "create datakey with custom key material", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyMaterial": { + "$binary": { + "base64": "a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs", + "subType": "00" + } + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with invalid custom key material (too short)", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyMaterial": { + "$binary": { + "base64": "a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs", + "subType": "00" + } + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/client-side-encryption/deleteKey.json b/tests/UnifiedSpecTests/client-side-encryption/deleteKey.json new file mode 100644 index 000000000..3a10fb082 --- /dev/null +++ b/tests/UnifiedSpecTests/client-side-encryption/deleteKey.json @@ -0,0 +1,557 @@ +{ + "description": "deleteKey", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "delete non-existent data key", + "operations": [ + { + "name": "deleteKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "deletedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "delete": "datakeys", + "deletes": [ + { + "q": { + "_id": { + "$binary": { + "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "limit": 1 + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + }, + { + "description": "delete existing AWS data key", + "operations": [ + { + "name": "deleteKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "delete": "datakeys", + "deletes": [ + { + "q": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "limit": 1 + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + }, + { + "description": "delete existing local data key", + "operations": [ + { + "name": "deleteKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "delete": "datakeys", + "deletes": [ + { + "q": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "limit": 1 + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + ] + } + ] + }, + { + "description": "delete existing data key twice", + "operations": [ + { + "name": "deleteKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "deleteKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "deletedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "delete": "datakeys", + "deletes": [ + { + "q": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "limit": 1 + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "delete": "datakeys", + "deletes": [ + { + "q": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "limit": 1 + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/client-side-encryption/getKey.json b/tests/UnifiedSpecTests/client-side-encryption/getKey.json new file mode 100644 index 000000000..2ea3fe735 --- /dev/null +++ b/tests/UnifiedSpecTests/client-side-encryption/getKey.json @@ -0,0 +1,319 @@ +{ + "description": "getKey", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "get non-existent data key", + "operations": [ + { + "name": "getKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "_id": { + "$binary": { + "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "get existing AWS data key", + "operations": [ + { + "name": "getKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + } + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "get existing local data key", + "operations": [ + { + "name": "getKey", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/client-side-encryption/getKeyByAltName.json b/tests/UnifiedSpecTests/client-side-encryption/getKeyByAltName.json new file mode 100644 index 000000000..2505abc16 --- /dev/null +++ b/tests/UnifiedSpecTests/client-side-encryption/getKeyByAltName.json @@ -0,0 +1,289 @@ +{ + "description": "getKeyByAltName", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "get non-existent data key", + "operations": [ + { + "name": "getKeyByAltName", + "object": "clientEncryption0", + "arguments": { + "keyAltName": "does_not_exist" + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": "does_not_exist" + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "get existing AWS data key", + "operations": [ + { + "name": "getKeyByAltName", + "object": "clientEncryption0", + "arguments": { + "keyAltName": "aws_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": "aws_key" + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "get existing local data key", + "operations": [ + { + "name": "getKeyByAltName", + "object": "clientEncryption0", + "arguments": { + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": "local_key" + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/client-side-encryption/getKeys.json b/tests/UnifiedSpecTests/client-side-encryption/getKeys.json new file mode 100644 index 000000000..d94471235 --- /dev/null +++ b/tests/UnifiedSpecTests/client-side-encryption/getKeys.json @@ -0,0 +1,260 @@ +{ + "description": "getKeys", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [] + } + ], + "tests": [ + { + "description": "getKeys with zero key documents", + "operations": [ + { + "name": "getKeys", + "object": "clientEncryption0", + "expectResult": [] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "getKeys with single key documents", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyAltNames": [ + "abc" + ] + } + }, + "expectResult": { + "$$type": "binData" + } + }, + { + "name": "getKeys", + "object": "clientEncryption0", + "expectResult": [ + { + "_id": { + "$$type": "binData" + }, + "keyAltNames": [ + "abc" + ], + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "getKeys with many key documents", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local" + }, + "expectResult": { + "$$type": "binData" + } + }, + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local" + }, + "expectResult": { + "$$type": "binData" + } + }, + { + "name": "getKeys", + "object": "clientEncryption0", + "expectResult": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + }, + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/client-side-encryption/removeKeyAltName.json b/tests/UnifiedSpecTests/client-side-encryption/removeKeyAltName.json new file mode 100644 index 000000000..1b7077077 --- /dev/null +++ b/tests/UnifiedSpecTests/client-side-encryption/removeKeyAltName.json @@ -0,0 +1,672 @@ +{ + "description": "removeKeyAltName", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "remove keyAltName from non-existent data key", + "operations": [ + { + "name": "removeKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "AAAjYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "does_not_exist" + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "AAAjYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": [ + { + "$set": { + "keyAltNames": { + "$cond": [ + { + "$eq": [ + "$keyAltNames", + [ + "does_not_exist" + ] + ] + }, + "$$REMOVE", + { + "$filter": { + "input": "$keyAltNames", + "cond": { + "$ne": [ + "$$this", + "does_not_exist" + ] + } + } + } + ] + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + }, + { + "description": "remove non-existent keyAltName from existing data key", + "operations": [ + { + "name": "removeKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "does_not_exist" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": [ + { + "$set": { + "keyAltNames": { + "$cond": [ + { + "$eq": [ + "$keyAltNames", + [ + "does_not_exist" + ] + ] + }, + "$$REMOVE", + { + "$filter": { + "input": "$keyAltNames", + "cond": { + "$ne": [ + "$$this", + "does_not_exist" + ] + } + } + } + ] + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "datakeys", + "databaseName": "keyvault", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ] + }, + { + "description": "remove an existing keyAltName from an existing data key", + "operations": [ + { + "name": "removeKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "alternate_name" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "projection": { + "_id": 0, + "keyAltNames": 1 + } + }, + "expectResult": [ + { + "keyAltNames": [ + "local_key" + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": [ + { + "$set": { + "keyAltNames": { + "$cond": [ + { + "$eq": [ + "$keyAltNames", + [ + "alternate_name" + ] + ] + }, + "$$REMOVE", + { + "$filter": { + "input": "$keyAltNames", + "cond": { + "$ne": [ + "$$this", + "alternate_name" + ] + } + } + } + ] + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "remove the last keyAltName from an existing data key", + "operations": [ + { + "name": "removeKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "alternate_name" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "alternate_name", + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + }, + { + "name": "removeKeyAltName", + "object": "clientEncryption0", + "arguments": { + "id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltName": "local_key" + }, + "expectResult": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": [ + { + "$set": { + "keyAltNames": { + "$cond": [ + { + "$eq": [ + "$keyAltNames", + [ + "alternate_name" + ] + ] + }, + "$$REMOVE", + { + "$filter": { + "input": "$keyAltNames", + "cond": { + "$ne": [ + "$$this", + "alternate_name" + ] + } + } + } + ] + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "findAndModify": "datakeys", + "query": { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + } + }, + "update": [ + { + "$set": { + "keyAltNames": { + "$cond": [ + { + "$eq": [ + "$keyAltNames", + [ + "local_key" + ] + ] + }, + "$$REMOVE", + { + "$filter": { + "input": "$keyAltNames", + "cond": { + "$ne": [ + "$$this", + "local_key" + ] + } + } + } + ] + } + } + } + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-decrypt_failure.json b/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-decrypt_failure.json new file mode 100644 index 000000000..4c7d4e804 --- /dev/null +++ b/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-decrypt_failure.json @@ -0,0 +1,162 @@ +{ + "description": "rewrapManyDataKey-decrypt_failure", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-2:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-2" + } + } + ] + } + ], + "tests": [ + { + "description": "rewrap data key that fails during decryption due to invalid masterKey", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": {}, + "opts": { + "provider": "local" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-encrypt_failure.json b/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-encrypt_failure.json new file mode 100644 index 000000000..cd2d20c25 --- /dev/null +++ b/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-encrypt_failure.json @@ -0,0 +1,250 @@ +{ + "description": "rewrapManyDataKey-encrypt_failure", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "rewrap with invalid masterKey for AWS KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": {}, + "opts": { + "provider": "aws", + "masterKey": { + "key": "arn:aws:kms:us-east-2:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-2" + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with invalid masterKey for Azure KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": {}, + "opts": { + "provider": "azure", + "masterKey": { + "keyVaultEndpoint": "invalid-vault-csfle.vault.azure.net", + "keyName": "invalid-name-csfle" + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with invalid masterKey for GCP KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": {}, + "opts": { + "provider": "gcp", + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "invalid-ring-csfle", + "keyName": "invalid-name-csfle" + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey.json b/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey.json new file mode 100644 index 000000000..6b3c9664a --- /dev/null +++ b/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey.json @@ -0,0 +1,1493 @@ +{ + "description": "rewrapManyDataKey", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "YXp1cmVhenVyZWF6dXJlYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "azure_key" + ], + "keyMaterial": { + "$binary": { + "base64": "pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", + "subType": "04" + } + }, + "keyAltNames": [ + "gcp_key" + ], + "keyMaterial": { + "$binary": { + "base64": "CiQAIgLj0USbQtof/pYRLQO96yg/JEtZbD1UxKueaC37yzT5tTkSiQEAhClWB5ZCSgzHgxv8raWjNB4r7e8ePGdsmSuYTYmLC5oHHS/BdQisConzNKFaobEQZHamTCjyhy5NotKF8MWoo+dyfQApwI29+vAGyrUIQCXzKwRnNdNQ+lb3vJtS5bqvLTvSxKHpVca2kqyC9nhonV+u4qru5Q2bAqUgVFc8fL4pBuvlowZFTQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "a21pcGttaXBrbWlwa21pcA==", + "subType": "04" + } + }, + "keyAltNames": [ + "kmip_key" + ], + "keyMaterial": { + "$binary": { + "base64": "CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "kmip", + "keyId": "1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "no keys to rewrap due to no filter matches", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": "no_matching_keys" + }, + "opts": { + "provider": "local" + } + }, + "expectResult": { + "bulkWriteResult": { + "$$exists": false + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": "no_matching_keys" + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new AWS KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "aws_key" + } + }, + "opts": { + "provider": "aws", + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "aws_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new Azure KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "azure_key" + } + }, + "opts": { + "provider": "azure", + "masterKey": { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "azure_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new GCP KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "gcp_key" + } + }, + "opts": { + "provider": "gcp", + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "gcp_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new KMIP KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "kmip_key" + } + }, + "opts": { + "provider": "kmip" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "kmip_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new local KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "local_key" + } + }, + "opts": { + "provider": "local" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "local_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with current KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": {} + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 5, + "modifiedCount": 5, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "projection": { + "masterKey": 1 + }, + "sort": { + "keyAltNames": 1 + } + }, + "expectResult": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "YXp1cmVhenVyZWF6dXJlYQ==", + "subType": "04" + } + }, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", + "subType": "04" + } + }, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "a21pcGttaXBrbWlwa21pcA==", + "subType": "04" + } + }, + "masterKey": { + "provider": "kmip", + "keyId": "1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/collection-management/clustered-indexes.json b/tests/UnifiedSpecTests/collection-management/clustered-indexes.json new file mode 100644 index 000000000..9db5ff06d --- /dev/null +++ b/tests/UnifiedSpecTests/collection-management/clustered-indexes.json @@ -0,0 +1,291 @@ +{ + "description": "clustered-indexes", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "5.3", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "ci-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "ci-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "createCollection with clusteredIndex", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "ci-tests", + "collectionName": "test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test" + }, + "databaseName": "ci-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + }, + "databaseName": "ci-tests" + } + } + ] + } + ] + }, + { + "description": "listCollections includes clusteredIndex", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + } + }, + { + "name": "listCollections", + "object": "database0", + "arguments": { + "filter": { + "name": { + "$eq": "test" + } + } + }, + "expectResult": [ + { + "name": "test", + "options": { + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index", + "v": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test" + }, + "databaseName": "ci-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + }, + "databaseName": "ci-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": { + "$eq": "test" + } + } + }, + "databaseName": "ci-tests" + } + } + ] + } + ] + }, + { + "description": "listIndexes returns the index", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + } + }, + { + "name": "listIndexes", + "object": "collection0", + "expectResult": [ + { + "key": { + "_id": 1 + }, + "name": "test index", + "clustered": true, + "unique": true, + "v": { + "$$type": [ + "int", + "long" + ] + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test" + }, + "databaseName": "ci-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true, + "name": "test index" + } + }, + "databaseName": "ci-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "test" + }, + "databaseName": "ci-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/collection-management/createCollection-pre_and_post_images.json b/tests/UnifiedSpecTests/collection-management/createCollection-pre_and_post_images.json new file mode 100644 index 000000000..f488deacd --- /dev/null +++ b/tests/UnifiedSpecTests/collection-management/createCollection-pre_and_post_images.json @@ -0,0 +1,92 @@ +{ + "description": "createCollection-pre_and_post_images", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "6.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "papi-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "createCollection with changeStreamPreAndPostImages enabled", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "test", + "changeStreamPreAndPostImages": { + "enabled": true + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "papi-tests", + "collectionName": "test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test" + }, + "databaseName": "papi-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "changeStreamPreAndPostImages": { + "enabled": true + } + }, + "databaseName": "papi-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/collection-management/modifyCollection-errorResponse.json b/tests/UnifiedSpecTests/collection-management/modifyCollection-errorResponse.json new file mode 100644 index 000000000..aea71eb08 --- /dev/null +++ b/tests/UnifiedSpecTests/collection-management/modifyCollection-errorResponse.json @@ -0,0 +1,118 @@ +{ + "description": "modifyCollection-errorResponse", + "schemaVersion": "1.12", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "collMod-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "collMod-tests", + "documents": [ + { + "_id": 1, + "x": 1 + }, + { + "_id": 2, + "x": 1 + } + ] + } + ], + "tests": [ + { + "description": "modifyCollection prepareUnique violations are accessible", + "runOnRequirements": [ + { + "minServerVersion": "5.2" + } + ], + "operations": [ + { + "name": "createIndex", + "object": "collection0", + "arguments": { + "keys": { + "x": 1 + } + } + }, + { + "name": "modifyCollection", + "object": "database0", + "arguments": { + "collection": "test", + "index": { + "keyPattern": { + "x": 1 + }, + "prepareUnique": true + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 3, + "x": 1 + } + }, + "expectError": { + "errorCode": 11000 + } + }, + { + "name": "modifyCollection", + "object": "database0", + "arguments": { + "collection": "test", + "index": { + "keyPattern": { + "x": 1 + }, + "unique": true + } + }, + "expectError": { + "isClientError": false, + "errorCode": 359, + "errorResponse": { + "violations": [ + { + "ids": [ + 1, + 2 + ] + } + ] + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/collection-management/modifyCollection-pre_and_post_images.json b/tests/UnifiedSpecTests/collection-management/modifyCollection-pre_and_post_images.json new file mode 100644 index 000000000..8026faeb1 --- /dev/null +++ b/tests/UnifiedSpecTests/collection-management/modifyCollection-pre_and_post_images.json @@ -0,0 +1,111 @@ +{ + "description": "modifyCollection-pre_and_post_images", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "6.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "papi-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "modifyCollection to changeStreamPreAndPostImages enabled", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "test", + "changeStreamPreAndPostImages": { + "enabled": false + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "papi-tests", + "collectionName": "test" + } + }, + { + "name": "modifyCollection", + "object": "database0", + "arguments": { + "collection": "test", + "changeStreamPreAndPostImages": { + "enabled": true + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test" + }, + "databaseName": "papi-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "changeStreamPreAndPostImages": { + "enabled": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "collMod": "test", + "changeStreamPreAndPostImages": { + "enabled": true + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/collection-management/timeseries-collection.json b/tests/UnifiedSpecTests/collection-management/timeseries-collection.json new file mode 100644 index 000000000..8525056fd --- /dev/null +++ b/tests/UnifiedSpecTests/collection-management/timeseries-collection.json @@ -0,0 +1,320 @@ +{ + "description": "timeseries-collection", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "ts-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "ts-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "createCollection with all options", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "test", + "expireAfterSeconds": 604800, + "timeseries": { + "timeField": "time", + "metaField": "meta", + "granularity": "minutes" + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "ts-tests", + "collectionName": "test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test" + }, + "databaseName": "ts-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "expireAfterSeconds": 604800, + "timeseries": { + "timeField": "time", + "metaField": "meta", + "granularity": "minutes" + } + }, + "databaseName": "ts-tests" + } + } + ] + } + ] + }, + { + "description": "insertMany with duplicate ids", + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "test", + "expireAfterSeconds": 604800, + "timeseries": { + "timeField": "time", + "metaField": "meta", + "granularity": "minutes" + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "ts-tests", + "collectionName": "test" + } + }, + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 1, + "time": { + "$date": { + "$numberLong": "1552949630482" + } + } + }, + { + "_id": 1, + "time": { + "$date": { + "$numberLong": "1552949630483" + } + } + } + ] + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "sort": { + "time": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "time": { + "$date": { + "$numberLong": "1552949630482" + } + } + }, + { + "_id": 1, + "time": { + "$date": { + "$numberLong": "1552949630483" + } + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test" + }, + "databaseName": "ts-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "expireAfterSeconds": 604800, + "timeseries": { + "timeField": "time", + "metaField": "meta", + "granularity": "minutes" + } + }, + "databaseName": "ts-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1, + "time": { + "$date": { + "$numberLong": "1552949630482" + } + } + }, + { + "_id": 1, + "time": { + "$date": { + "$numberLong": "1552949630483" + } + } + } + ] + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": {}, + "sort": { + "time": 1 + } + }, + "databaseName": "ts-tests" + } + } + ] + } + ] + }, + { + "description": "createCollection with bucketing options", + "runOnRequirements": [ + { + "minServerVersion": "7.0" + } + ], + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "test", + "timeseries": { + "timeField": "time", + "bucketMaxSpanSeconds": 3600, + "bucketRoundingSeconds": 3600 + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "ts-tests", + "collectionName": "test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test" + }, + "databaseName": "ts-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "timeseries": { + "timeField": "time", + "bucketMaxSpanSeconds": 3600, + "bucketRoundingSeconds": 3600 + } + }, + "databaseName": "ts-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/bulkWrite.json b/tests/UnifiedSpecTests/command-monitoring/bulkWrite.json new file mode 100644 index 000000000..49c728442 --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/bulkWrite.json @@ -0,0 +1,154 @@ +{ + "description": "bulkWrite", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "command-monitoring-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "A successful mixed bulk write", + "operations": [ + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 4, + "x": 44 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 3 + }, + "update": { + "$set": { + "x": 333 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 4, + "x": 44 + } + ], + "ordered": true + }, + "commandName": "insert", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 3 + }, + "u": { + "$set": { + "x": 333 + } + }, + "upsert": { + "$$unsetOrMatches": false + }, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true + }, + "commandName": "update", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "update" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/command.json b/tests/UnifiedSpecTests/command-monitoring/command.json new file mode 100644 index 000000000..c28af95fe --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/command.json @@ -0,0 +1,83 @@ +{ + "description": "command", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "command-monitoring-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "A successful command", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "commandName": "ping", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1 + }, + "commandName": "ping" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/deleteMany.json b/tests/UnifiedSpecTests/command-monitoring/deleteMany.json new file mode 100644 index 000000000..78ebad1f9 --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/deleteMany.json @@ -0,0 +1,162 @@ +{ + "description": "deleteMany", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "command-monitoring-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "A successful deleteMany", + "operations": [ + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "limit": 0 + } + ], + "ordered": true + }, + "commandName": "delete", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 2 + }, + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "A successful deleteMany with write errors", + "operations": [ + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$unsupported": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": { + "$unsupported": 1 + } + }, + "limit": 0 + } + ], + "ordered": true + }, + "commandName": "delete", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 0, + "writeErrors": { + "$$type": "array" + } + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/deleteOne.json b/tests/UnifiedSpecTests/command-monitoring/deleteOne.json new file mode 100644 index 000000000..2420794fe --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/deleteOne.json @@ -0,0 +1,162 @@ +{ + "description": "deleteOne", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "command-monitoring-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "A successful deleteOne", + "operations": [ + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "limit": 1 + } + ], + "ordered": true + }, + "commandName": "delete", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "A successful deleteOne with write errors", + "operations": [ + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$unsupported": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": { + "$unsupported": 1 + } + }, + "limit": 1 + } + ], + "ordered": true + }, + "commandName": "delete", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 0, + "writeErrors": { + "$$type": "array" + } + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/find.json b/tests/UnifiedSpecTests/command-monitoring/find.json new file mode 100644 index 000000000..4b5f45ae9 --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/find.json @@ -0,0 +1,550 @@ +{ + "description": "find", + "schemaVersion": "1.1", + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "_yamlAnchors": { + "namespace": "command-monitoring-tests.test" + }, + "initialData": [ + { + "collectionName": "test", + "databaseName": "command-monitoring-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "tests": [ + { + "description": "A successful find with no options", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": 1 + } + }, + "commandName": "find", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "cursor": { + "id": 0, + "ns": "command-monitoring-tests.test", + "firstBatch": [ + { + "_id": 1, + "x": 11 + } + ] + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "A successful find with options", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "x": -1 + }, + "projection": { + "_id": 0, + "x": 1 + }, + "skip": 2, + "comment": "test", + "hint": { + "_id": 1 + }, + "max": { + "_id": 6 + }, + "maxTimeMS": 6000, + "min": { + "_id": 0 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "x": -1 + }, + "projection": { + "_id": 0, + "x": 1 + }, + "skip": 2, + "comment": "test", + "hint": { + "_id": 1 + }, + "max": { + "_id": 6 + }, + "maxTimeMS": 6000, + "min": { + "_id": 0 + } + }, + "commandName": "find", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "cursor": { + "id": 0, + "ns": "command-monitoring-tests.test", + "firstBatch": [ + { + "x": 33 + }, + { + "x": 22 + } + ] + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "A successful find with showRecordId and returnKey", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "showRecordId": true, + "returnKey": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "showRecordId": true, + "returnKey": true + }, + "commandName": "find", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "cursor": { + "id": 0, + "ns": "command-monitoring-tests.test", + "firstBatch": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + }, + { + "_id": 5 + } + ] + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "A successful find with a getMore", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$gte": 1 + } + }, + "sort": { + "_id": 1 + }, + "batchSize": 3 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": { + "$gte": 1 + } + }, + "sort": { + "_id": 1 + }, + "batchSize": 3 + }, + "commandName": "find", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "cursor": { + "id": { + "$$type": [ + "int", + "long" + ] + }, + "ns": "command-monitoring-tests.test", + "firstBatch": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3 + }, + "commandName": "getMore", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "cursor": { + "id": 0, + "ns": "command-monitoring-tests.test", + "nextBatch": [ + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + }, + "commandName": "getMore" + } + } + ] + } + ] + }, + { + "description": "A successful find event with a getmore and the server kills the cursor (<= 4.4)", + "runOnRequirements": [ + { + "minServerVersion": "3.1", + "maxServerVersion": "4.4.99", + "topologies": [ + "single", + "replicaset" + ] + } + ], + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$gte": 1 + } + }, + "sort": { + "_id": 1 + }, + "batchSize": 3, + "limit": 4 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": { + "$gte": 1 + } + }, + "sort": { + "_id": 1 + }, + "batchSize": 3, + "limit": 4 + }, + "commandName": "find", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "cursor": { + "id": { + "$$type": [ + "int", + "long" + ] + }, + "ns": "command-monitoring-tests.test", + "firstBatch": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 1 + }, + "commandName": "getMore", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "cursor": { + "id": 0, + "ns": "command-monitoring-tests.test", + "nextBatch": [ + { + "_id": 4, + "x": 44 + } + ] + } + }, + "commandName": "getMore" + } + } + ] + } + ] + }, + { + "description": "A failed find event", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "$or": true + } + }, + "commandName": "find", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/insertMany.json b/tests/UnifiedSpecTests/command-monitoring/insertMany.json new file mode 100644 index 000000000..a80a218c6 --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/insertMany.json @@ -0,0 +1,148 @@ +{ + "description": "insertMany", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "command-monitoring-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "A successful insertMany", + "operations": [ + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "ordered": true + }, + "commandName": "insert", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "A successful insertMany with write errors", + "operations": [ + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1, + "x": 11 + } + ], + "ordered": true + }, + "commandName": "insert", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 0, + "writeErrors": { + "$$type": "array" + } + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/insertOne.json b/tests/UnifiedSpecTests/command-monitoring/insertOne.json new file mode 100644 index 000000000..6ff732e41 --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/insertOne.json @@ -0,0 +1,144 @@ +{ + "description": "insertOne", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "command-monitoring-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "A successful insertOne", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "ordered": true + }, + "commandName": "insert", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "A successful insertOne with write errors", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1, + "x": 11 + } + ], + "ordered": true + }, + "commandName": "insert", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 0, + "writeErrors": { + "$$type": "array" + } + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/pre-42-server-connection-id.json b/tests/UnifiedSpecTests/command-monitoring/pre-42-server-connection-id.json new file mode 100644 index 000000000..141fbe584 --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/pre-42-server-connection-id.json @@ -0,0 +1,101 @@ +{ + "description": "pre-42-server-connection-id", + "schemaVersion": "1.6", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "server-connection-id-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "databaseName": "server-connection-id-tests", + "collectionName": "coll", + "documents": [] + } + ], + "tests": [ + { + "description": "command events do not include server connection id", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "hasServerConnectionId": false + } + }, + { + "commandSucceededEvent": { + "commandName": "insert", + "hasServerConnectionId": false + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "hasServerConnectionId": false + } + }, + { + "commandFailedEvent": { + "commandName": "find", + "hasServerConnectionId": false + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/redacted-commands.json b/tests/UnifiedSpecTests/command-monitoring/redacted-commands.json new file mode 100644 index 000000000..4302ba890 --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/redacted-commands.json @@ -0,0 +1,679 @@ +{ + "description": "redacted-commands", + "schemaVersion": "1.5", + "runOnRequirements": [ + { + "minServerVersion": "5.0", + "auth": false + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ], + "observeSensitiveCommands": true + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + } + ], + "tests": [ + { + "description": "authenticate", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "authenticate", + "command": { + "authenticate": 1, + "mechanism": "MONGODB-X509", + "user": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry", + "db": "$external" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "authenticate", + "command": { + "authenticate": { + "$$exists": false + }, + "mechanism": { + "$$exists": false + }, + "user": { + "$$exists": false + }, + "db": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "saslStart", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslStart", + "command": { + "saslStart": 1, + "payload": "definitely-invalid-payload", + "db": "admin" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "saslStart", + "command": { + "saslStart": { + "$$exists": false + }, + "payload": { + "$$exists": false + }, + "db": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "saslContinue", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslContinue", + "command": { + "saslContinue": 1, + "conversationId": 0, + "payload": "definitely-invalid-payload" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "saslContinue", + "command": { + "saslContinue": { + "$$exists": false + }, + "conversationId": { + "$$exists": false + }, + "payload": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "getnonce", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "getnonce", + "command": { + "getnonce": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "getnonce", + "command": { + "getnonce": { + "$$exists": false + } + } + } + }, + { + "commandSucceededEvent": { + "commandName": "getnonce", + "reply": { + "ok": { + "$$exists": false + }, + "nonce": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "createUser", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "createUser", + "command": { + "createUser": "private", + "pwd": {}, + "roles": [] + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createUser", + "command": { + "createUser": { + "$$exists": false + }, + "pwd": { + "$$exists": false + }, + "roles": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "updateUser", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "updateUser", + "command": { + "updateUser": "private", + "pwd": {}, + "roles": [] + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "updateUser", + "command": { + "updateUser": { + "$$exists": false + }, + "pwd": { + "$$exists": false + }, + "roles": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "copydbgetnonce", + "runOnRequirements": [ + { + "maxServerVersion": "3.6.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbgetnonce", + "command": { + "copydbgetnonce": "private" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "copydbgetnonce", + "command": { + "copydbgetnonce": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "copydbsaslstart", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbsaslstart", + "command": { + "copydbsaslstart": "private" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "copydbsaslstart", + "command": { + "copydbsaslstart": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "copydb", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydb", + "command": { + "copydb": "private" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "copydb", + "command": { + "copydb": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "hello with speculative authenticate", + "runOnRequirements": [ + { + "minServerVersion": "4.9" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "hello", + "command": { + "hello": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + }, + { + "commandSucceededEvent": { + "commandName": "hello", + "reply": { + "isWritablePrimary": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "legacy hello with speculative authenticate", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ismaster", + "command": { + "ismaster": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + }, + { + "commandSucceededEvent": { + "commandName": "ismaster", + "reply": { + "ismaster": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "isMaster", + "command": { + "isMaster": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + }, + { + "commandSucceededEvent": { + "commandName": "isMaster", + "reply": { + "ismaster": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "hello without speculative authenticate is not redacted", + "runOnRequirements": [ + { + "minServerVersion": "4.9" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "hello", + "command": { + "hello": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "hello", + "reply": { + "isWritablePrimary": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "legacy hello without speculative authenticate is not redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": 1 + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ismaster", + "command": { + "ismaster": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "ismaster", + "reply": { + "ismaster": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "isMaster", + "command": { + "isMaster": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "isMaster", + "reply": { + "ismaster": { + "$$exists": true + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/server-connection-id.json b/tests/UnifiedSpecTests/command-monitoring/server-connection-id.json new file mode 100644 index 000000000..a8f27637f --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/server-connection-id.json @@ -0,0 +1,101 @@ +{ + "description": "server-connection-id", + "schemaVersion": "1.6", + "runOnRequirements": [ + { + "minServerVersion": "4.2" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "server-connection-id-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "databaseName": "server-connection-id-tests", + "collectionName": "coll", + "documents": [] + } + ], + "tests": [ + { + "description": "command events include server connection id", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "hasServerConnectionId": true + } + }, + { + "commandSucceededEvent": { + "commandName": "insert", + "hasServerConnectionId": true + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "hasServerConnectionId": true + } + }, + { + "commandFailedEvent": { + "commandName": "find", + "hasServerConnectionId": true + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/unacknowledgedBulkWrite.json b/tests/UnifiedSpecTests/command-monitoring/unacknowledgedBulkWrite.json new file mode 100644 index 000000000..4c16d6df1 --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/unacknowledgedBulkWrite.json @@ -0,0 +1,108 @@ +{ + "description": "unacknowledgedBulkWrite", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "command-monitoring-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "A successful unordered bulk write with an unacknowledged write concern", + "operations": [ + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": "unorderedBulkWriteInsertW0", + "x": 44 + } + } + } + ], + "ordered": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": "unorderedBulkWriteInsertW0", + "x": 44 + } + ], + "ordered": false, + "writeConcern": { + "w": 0 + } + }, + "commandName": "insert", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": { + "$$exists": false + } + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/updateMany.json b/tests/UnifiedSpecTests/command-monitoring/updateMany.json new file mode 100644 index 000000000..b15434226 --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/updateMany.json @@ -0,0 +1,188 @@ +{ + "description": "updateMany", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "command-monitoring-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "A successful updateMany", + "operations": [ + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "upsert": { + "$$unsetOrMatches": false + }, + "multi": true + } + ], + "ordered": true + }, + "commandName": "update", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 2 + }, + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "A successful updateMany with write errors", + "operations": [ + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$unsupported": { + "x": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$unsupported": { + "x": 1 + } + }, + "upsert": { + "$$unsetOrMatches": false + }, + "multi": true + } + ], + "ordered": true + }, + "commandName": "update", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 0, + "writeErrors": { + "$$type": "array" + } + }, + "commandName": "update" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/updateOne.json b/tests/UnifiedSpecTests/command-monitoring/updateOne.json new file mode 100644 index 000000000..a0ae99e88 --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/updateOne.json @@ -0,0 +1,260 @@ +{ + "description": "updateOne", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "command-monitoring-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "A successful updateOne", + "operations": [ + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "upsert": { + "$$unsetOrMatches": false + }, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true + }, + "commandName": "update", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "A successful updateOne with upsert where the upserted id is not an ObjectId", + "operations": [ + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 4 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "upsert": true, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true + }, + "commandName": "update", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1, + "upserted": [ + { + "index": 0, + "_id": 4 + } + ] + }, + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "A successful updateOne with write errors", + "operations": [ + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$unsupported": { + "x": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$unsupported": { + "x": 1 + } + }, + "upsert": { + "$$unsetOrMatches": false + }, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true + }, + "commandName": "update", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 0, + "writeErrors": { + "$$type": "array" + } + }, + "commandName": "update" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/command-monitoring/writeConcernError.json b/tests/UnifiedSpecTests/command-monitoring/writeConcernError.json new file mode 100644 index 000000000..7bc16f2ab --- /dev/null +++ b/tests/UnifiedSpecTests/command-monitoring/writeConcernError.json @@ -0,0 +1,155 @@ +{ + "description": "writeConcernError", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.1.0", + "topologies": [ + "replicaset" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "command-monitoring-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "A retryable write with write concern errors publishes success event", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "writeConcernError": { + "code": 91, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "ordered": true + }, + "commandName": "insert", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1, + "writeConcernError": { + "code": 91, + "errorLabels": [ + "RetryableWriteError" + ] + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "ordered": true + }, + "commandName": "insert", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/aggregate-allowdiskuse.json b/tests/UnifiedSpecTests/crud/aggregate-allowdiskuse.json new file mode 100644 index 000000000..2e54175b8 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/aggregate-allowdiskuse.json @@ -0,0 +1,155 @@ +{ + "description": "aggregate-allowdiskuse", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Aggregate does not send allowDiskUse when value is not specified", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": {} + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": {} + } + ], + "allowDiskUse": { + "$$exists": false + } + }, + "commandName": "aggregate", + "databaseName": "crud-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate sends allowDiskUse false when false is specified", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": {} + } + ], + "allowDiskUse": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": {} + } + ], + "allowDiskUse": false + }, + "commandName": "aggregate", + "databaseName": "crud-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate sends allowDiskUse true when true is specified", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": {} + } + ], + "allowDiskUse": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": {} + } + ], + "allowDiskUse": true + }, + "commandName": "aggregate", + "databaseName": "crud-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/aggregate-let.json b/tests/UnifiedSpecTests/crud/aggregate-let.json new file mode 100644 index 000000000..039900920 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/aggregate-let.json @@ -0,0 +1,376 @@ +{ + "description": "aggregate-let", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + }, + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "coll1" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + }, + { + "collectionName": "coll1", + "databaseName": "crud-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Aggregate with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + } + }, + { + "$project": { + "_id": 0, + "x": "$$x", + "y": "$$y", + "rand": "$$rand" + } + } + ], + "let": { + "id": 1, + "x": "foo", + "y": { + "$literal": "$bar" + }, + "rand": { + "$rand": {} + } + } + }, + "expectResult": [ + { + "x": "foo", + "y": "$bar", + "rand": { + "$$type": "double" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + } + }, + { + "$project": { + "_id": 0, + "x": "$$x", + "y": "$$y", + "rand": "$$rand" + } + } + ], + "let": { + "id": 1, + "x": "foo", + "y": { + "$literal": "$bar" + }, + "rand": { + "$rand": {} + } + } + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "2.6.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ], + "let": { + "x": "foo" + } + }, + "expectError": { + "errorContains": "unrecognized field 'let'", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ], + "let": { + "x": "foo" + } + } + } + } + ] + } + ] + }, + { + "description": "Aggregate to collection with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0", + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$out": "coll1" + } + ], + "let": { + "id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$out": "coll1" + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll1", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Aggregate to collection with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "2.6.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$out": "coll1" + } + ], + "let": { + "id": 1 + } + }, + "expectError": { + "errorContains": "unrecognized field 'let'", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$out": "coll1" + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/aggregate-merge-errorResponse.json b/tests/UnifiedSpecTests/crud/aggregate-merge-errorResponse.json new file mode 100644 index 000000000..6c7305fd9 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/aggregate-merge-errorResponse.json @@ -0,0 +1,90 @@ +{ + "description": "aggregate-merge-errorResponse", + "schemaVersion": "1.12", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 1 + }, + { + "_id": 2, + "x": 1 + } + ] + } + ], + "tests": [ + { + "description": "aggregate $merge DuplicateKey error is accessible", + "runOnRequirements": [ + { + "minServerVersion": "5.1", + "topologies": [ + "single", + "replicaset" + ] + } + ], + "operations": [ + { + "name": "aggregate", + "object": "database0", + "arguments": { + "pipeline": [ + { + "$documents": [ + { + "_id": 2, + "x": 1 + } + ] + }, + { + "$merge": { + "into": "test", + "whenMatched": "fail" + } + } + ] + }, + "expectError": { + "errorCode": 11000, + "errorResponse": { + "keyPattern": { + "_id": 1 + }, + "keyValue": { + "_id": 2 + } + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/aggregate-merge.json b/tests/UnifiedSpecTests/crud/aggregate-merge.json new file mode 100644 index 000000000..ac61ceb8a --- /dev/null +++ b/tests/UnifiedSpecTests/crud/aggregate-merge.json @@ -0,0 +1,497 @@ +{ + "description": "aggregate-merge", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.1.11" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_aggregate_merge" + } + }, + { + "collection": { + "id": "collection_readConcern_majority", + "database": "database0", + "collectionName": "test_aggregate_merge", + "collectionOptions": { + "readConcern": { + "level": "majority" + } + } + } + }, + { + "collection": { + "id": "collection_readConcern_local", + "database": "database0", + "collectionName": "test_aggregate_merge", + "collectionOptions": { + "readConcern": { + "level": "local" + } + } + } + }, + { + "collection": { + "id": "collection_readConcern_available", + "database": "database0", + "collectionName": "test_aggregate_merge", + "collectionOptions": { + "readConcern": { + "level": "available" + } + } + } + } + ], + "initialData": [ + { + "collectionName": "test_aggregate_merge", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Aggregate with $merge", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_test_collection" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test_aggregate_merge", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_test_collection" + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "other_test_collection", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "Aggregate with $merge and batch size of 0", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_test_collection" + } + } + ], + "batchSize": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test_aggregate_merge", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_test_collection" + } + } + ], + "cursor": {} + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "other_test_collection", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "Aggregate with $merge and majority readConcern", + "operations": [ + { + "object": "collection_readConcern_majority", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_test_collection" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test_aggregate_merge", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_test_collection" + } + } + ], + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "other_test_collection", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "Aggregate with $merge and local readConcern", + "operations": [ + { + "object": "collection_readConcern_local", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_test_collection" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test_aggregate_merge", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_test_collection" + } + } + ], + "readConcern": { + "level": "local" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "other_test_collection", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "Aggregate with $merge and available readConcern", + "operations": [ + { + "object": "collection_readConcern_available", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_test_collection" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test_aggregate_merge", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_test_collection" + } + } + ], + "readConcern": { + "level": "available" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "other_test_collection", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/aggregate-out-readConcern.json b/tests/UnifiedSpecTests/crud/aggregate-out-readConcern.json new file mode 100644 index 000000000..e293457c1 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/aggregate-out-readConcern.json @@ -0,0 +1,407 @@ +{ + "description": "aggregate-out-readConcern", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.1.0", + "topologies": [ + "replicaset", + "sharded" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_aggregate_out_readconcern" + } + }, + { + "collection": { + "id": "collection_readConcern_majority", + "database": "database0", + "collectionName": "test_aggregate_out_readconcern", + "collectionOptions": { + "readConcern": { + "level": "majority" + } + } + } + }, + { + "collection": { + "id": "collection_readConcern_local", + "database": "database0", + "collectionName": "test_aggregate_out_readconcern", + "collectionOptions": { + "readConcern": { + "level": "local" + } + } + } + }, + { + "collection": { + "id": "collection_readConcern_available", + "database": "database0", + "collectionName": "test_aggregate_out_readconcern", + "collectionOptions": { + "readConcern": { + "level": "available" + } + } + } + }, + { + "collection": { + "id": "collection_readConcern_linearizable", + "database": "database0", + "collectionName": "test_aggregate_out_readconcern", + "collectionOptions": { + "readConcern": { + "level": "linearizable" + } + } + } + } + ], + "initialData": [ + { + "collectionName": "test_aggregate_out_readconcern", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "readConcern majority with out stage", + "operations": [ + { + "object": "collection_readConcern_majority", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test_aggregate_out_readconcern", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ], + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "other_test_collection", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "readConcern local with out stage", + "operations": [ + { + "object": "collection_readConcern_local", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test_aggregate_out_readconcern", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ], + "readConcern": { + "level": "local" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "other_test_collection", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "readConcern available with out stage", + "operations": [ + { + "object": "collection_readConcern_available", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test_aggregate_out_readconcern", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ], + "readConcern": { + "level": "available" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "other_test_collection", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "readConcern linearizable with out stage", + "operations": [ + { + "object": "collection_readConcern_linearizable", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test_aggregate_out_readconcern", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ], + "readConcern": { + "level": "linearizable" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/aggregate-write-readPreference.json b/tests/UnifiedSpecTests/crud/aggregate-write-readPreference.json new file mode 100644 index 000000000..bc887e83c --- /dev/null +++ b/tests/UnifiedSpecTests/crud/aggregate-write-readPreference.json @@ -0,0 +1,460 @@ +{ + "description": "aggregate-write-readPreference", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "_yamlAnchors": { + "readConcern": { + "level": "local" + }, + "writeConcern": { + "w": 1 + } + }, + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "uriOptions": { + "readConcernLevel": "local", + "w": 1 + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "readPreference": { + "mode": "secondaryPreferred", + "maxStalenessSeconds": 600 + } + } + } + }, + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "coll1" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + }, + { + "collectionName": "coll1", + "databaseName": "db0", + "documents": [] + } + ], + "tests": [ + { + "description": "Aggregate with $out includes read preference for 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0", + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$out": "coll1" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$out": "coll1" + } + ], + "$readPreference": { + "mode": "secondaryPreferred", + "maxStalenessSeconds": 600 + }, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "w": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll1", + "databaseName": "db0", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "Aggregate with $out omits read preference for pre-5.0 server", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.4.99", + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$out": "coll1" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$out": "coll1" + } + ], + "$readPreference": { + "$$exists": false + }, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "w": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll1", + "databaseName": "db0", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "Aggregate with $merge includes read preference for 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$merge": { + "into": "coll1" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$merge": { + "into": "coll1" + } + } + ], + "$readPreference": { + "mode": "secondaryPreferred", + "maxStalenessSeconds": 600 + }, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "w": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll1", + "databaseName": "db0", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "Aggregate with $merge omits read preference for pre-5.0 server", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$merge": { + "into": "coll1" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$merge": { + "into": "coll1" + } + } + ], + "$readPreference": { + "$$exists": false + }, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "w": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll1", + "databaseName": "db0", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/aggregate.json b/tests/UnifiedSpecTests/crud/aggregate.json new file mode 100644 index 000000000..0cbfb4e6e --- /dev/null +++ b/tests/UnifiedSpecTests/crud/aggregate.json @@ -0,0 +1,567 @@ +{ + "description": "aggregate", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "aggregate-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "aggregate-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "tests": [ + { + "description": "aggregate with multiple batches works", + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "batchSize": 2 + }, + "object": "collection0", + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "cursor": { + "batchSize": 2 + } + }, + "commandName": "aggregate", + "databaseName": "aggregate-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2 + }, + "commandName": "getMore", + "databaseName": "aggregate-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2 + }, + "commandName": "getMore", + "databaseName": "aggregate-tests" + } + } + ] + } + ] + }, + { + "description": "aggregate with a string comment", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0" + } + ], + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": "comment" + }, + "object": "collection0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": "comment" + } + } + } + ] + } + ] + }, + { + "description": "aggregate with a document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": { + "content": "test" + } + }, + "object": "collection0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": { + "content": "test" + } + } + } + } + ] + } + ] + }, + { + "description": "aggregate with a document comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": { + "content": "test" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": { + "content": "test" + } + }, + "commandName": "aggregate", + "databaseName": "aggregate-tests" + } + } + ] + } + ] + }, + { + "description": "aggregate with comment sets comment on getMore", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "batchSize": 2, + "comment": { + "content": "test" + } + }, + "object": "collection0", + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "cursor": { + "batchSize": 2 + }, + "comment": { + "content": "test" + } + }, + "commandName": "aggregate", + "databaseName": "aggregate-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "content": "test" + } + }, + "commandName": "getMore", + "databaseName": "aggregate-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "content": "test" + } + }, + "commandName": "getMore", + "databaseName": "aggregate-tests" + } + } + ] + } + ] + }, + { + "description": "aggregate with comment does not set comment on getMore - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.3.99" + } + ], + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "batchSize": 2, + "comment": "comment" + }, + "object": "collection0", + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "cursor": { + "batchSize": 2 + }, + "comment": "comment" + }, + "commandName": "aggregate", + "databaseName": "aggregate-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "$$exists": false + } + }, + "commandName": "getMore", + "databaseName": "aggregate-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "$$exists": false + } + }, + "commandName": "getMore", + "databaseName": "aggregate-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/SpecTests/crud/bulkWrite-arrayFilters-clientError.json b/tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters-clientError.json similarity index 53% rename from tests/SpecTests/crud/bulkWrite-arrayFilters-clientError.json rename to tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters-clientError.json index 22e22f0ef..63815e323 100644 --- a/tests/SpecTests/crud/bulkWrite-arrayFilters-clientError.json +++ b/tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters-clientError.json @@ -1,29 +1,61 @@ { - "runOn": [ + "description": "bulkWrite-arrayFilters-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ { "maxServerVersion": "3.5.5" } ], - "data": [ + "createEntities": [ { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } }, { - "_id": 2, - "y": [ + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "crud-v2" + } + } + ], + "initialData": [ + { + "collectionName": "crud-v2", + "databaseName": "crud-v2", + "documents": [ { - "b": 0 + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] }, { - "b": 1 + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] } ] } @@ -33,12 +65,12 @@ "description": "BulkWrite on server that doesn't support arrayFilters", "operations": [ { + "object": "collection0", "name": "bulkWrite", "arguments": { "requests": [ { - "name": "updateOne", - "arguments": { + "updateOne": { "filter": {}, "update": { "$set": { @@ -53,25 +85,30 @@ } } ], - "options": { - "ordered": true - } + "ordered": true }, - "error": true + "expectError": { + "isError": true + } } ], - "expectations": [] + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] }, { "description": "BulkWrite on server that doesn't support arrayFilters with arrayFilters on second op", "operations": [ { + "object": "collection0", "name": "bulkWrite", "arguments": { "requests": [ { - "name": "updateOne", - "arguments": { + "updateOne": { "filter": {}, "update": { "$set": { @@ -81,8 +118,7 @@ } }, { - "name": "updateMany", - "arguments": { + "updateMany": { "filter": {}, "update": { "$set": { @@ -97,14 +133,19 @@ } } ], - "options": { - "ordered": true - } + "ordered": true }, - "error": true + "expectError": { + "isError": true + } } ], - "expectations": [] + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] } ] } diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters.json b/tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters.json new file mode 100644 index 000000000..70ee014f7 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters.json @@ -0,0 +1,279 @@ +{ + "description": "bulkWrite-arrayFilters", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.5.6" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite updateOne with arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 3 + } + ] + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": {}, + "u": { + "$set": { + "y.$[i].b": 2 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "arrayFilters": [ + { + "i.b": 3 + } + ] + } + ], + "ordered": true + }, + "commandName": "update", + "databaseName": "crud-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 2 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + } + ] + } + ] + }, + { + "description": "BulkWrite updateMany with arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 1 + } + ] + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": {}, + "u": { + "$set": { + "y.$[i].b": 2 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + }, + "arrayFilters": [ + { + "i.b": 1 + } + ] + } + ], + "ordered": true + }, + "commandName": "update", + "databaseName": "crud-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 2 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 2 + } + ] + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-comment.json b/tests/UnifiedSpecTests/crud/bulkWrite-comment.json new file mode 100644 index 000000000..0b2addc85 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-comment.json @@ -0,0 +1,519 @@ +{ + "description": "bulkWrite-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "BulkWrite_comment" + } + } + ], + "initialData": [ + { + "collectionName": "BulkWrite_comment", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 5, + "x": "inserted" + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": "replaced" + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$set": { + "x": "updated" + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + } + ], + "comment": "comment" + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 5 + } + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "BulkWrite_comment", + "documents": [ + { + "_id": 5, + "x": "inserted" + } + ], + "ordered": true, + "comment": "comment" + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "BulkWrite_comment", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "x": "replaced" + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": 2 + }, + "u": { + "$set": { + "x": "updated" + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "comment": "comment" + } + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "BulkWrite_comment", + "deletes": [ + { + "q": { + "_id": 3 + }, + "limit": 1 + } + ], + "ordered": true, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_comment", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": "replaced" + }, + { + "_id": 2, + "x": "updated" + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": "inserted" + } + ] + } + ] + }, + { + "description": "BulkWrite with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 5, + "x": "inserted" + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": "replaced" + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$set": { + "x": "updated" + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + } + ], + "comment": { + "key": "value" + } + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 5 + } + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "BulkWrite_comment", + "documents": [ + { + "_id": 5, + "x": "inserted" + } + ], + "ordered": true, + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "BulkWrite_comment", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "x": "replaced" + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": 2 + }, + "u": { + "$set": { + "x": "updated" + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "BulkWrite_comment", + "deletes": [ + { + "q": { + "_id": 3 + }, + "limit": 1 + } + ], + "ordered": true, + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_comment", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": "replaced" + }, + { + "_id": 2, + "x": "updated" + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": "inserted" + } + ] + } + ] + }, + { + "description": "BulkWrite with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 5, + "x": "inserted" + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": "replaced" + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$set": { + "x": "updated" + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + } + ], + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "BulkWrite_comment", + "documents": [ + { + "_id": 5, + "x": "inserted" + } + ], + "ordered": true, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_comment", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + } + ] +} diff --git a/tests/SpecTests/crud/bulkWrite-delete-hint-clientError.json b/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-clientError.json similarity index 54% rename from tests/SpecTests/crud/bulkWrite-delete-hint-clientError.json rename to tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-clientError.json index cfeac904c..2961b55dc 100644 --- a/tests/SpecTests/crud/bulkWrite-delete-hint-clientError.json +++ b/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-clientError.json @@ -1,39 +1,70 @@ { - "runOn": [ + "description": "bulkWrite-delete-hint-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ { "maxServerVersion": "3.3.99" } ], - "data": [ + "createEntities": [ { - "_id": 1, - "x": 11 + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } }, { - "_id": 2, - "x": 22 + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } }, { - "_id": 3, - "x": 33 - }, + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "BulkWrite_delete_hint" + } + } + ], + "initialData": [ { - "_id": 4, - "x": 44 + "collectionName": "BulkWrite_delete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] } ], - "collection_name": "BulkWrite_delete_hint", "tests": [ { "description": "BulkWrite deleteOne with hints unsupported (client-side error)", "operations": [ { + "object": "collection0", "name": "bulkWrite", "arguments": { "requests": [ { - "name": "deleteOne", - "arguments": { + "deleteOne": { "filter": { "_id": 1 }, @@ -41,8 +72,7 @@ } }, { - "name": "deleteOne", - "arguments": { + "deleteOne": { "filter": { "_id": 2 }, @@ -52,17 +82,24 @@ } } ], - "options": { - "ordered": true - } + "ordered": true }, - "error": true + "expectError": { + "isError": true + } } ], - "expectations": [], - "outcome": { - "collection": { - "data": [ + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_delete_hint", + "databaseName": "crud-v2", + "documents": [ { "_id": 1, "x": 11 @@ -81,18 +118,18 @@ } ] } - } + ] }, { "description": "BulkWrite deleteMany with hints unsupported (client-side error)", "operations": [ { + "object": "collection0", "name": "bulkWrite", "arguments": { "requests": [ { - "name": "deleteMany", - "arguments": { + "deleteMany": { "filter": { "_id": { "$lt": 3 @@ -102,8 +139,7 @@ } }, { - "name": "deleteMany", - "arguments": { + "deleteMany": { "filter": { "_id": { "$gte": 4 @@ -115,17 +151,24 @@ } } ], - "options": { - "ordered": true - } + "ordered": true }, - "error": true + "expectError": { + "isError": true + } } ], - "expectations": [], - "outcome": { - "collection": { - "data": [ + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_delete_hint", + "databaseName": "crud-v2", + "documents": [ { "_id": 1, "x": 11 @@ -144,7 +187,7 @@ } ] } - } + ] } ] } diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-serverError.json b/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-serverError.json new file mode 100644 index 000000000..fa9952209 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-serverError.json @@ -0,0 +1,252 @@ +{ + "description": "bulkWrite-delete-hint-serverError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.3.3" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "BulkWrite_delete_hint" + } + } + ], + "initialData": [ + { + "collectionName": "BulkWrite_delete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite deleteOne with hints unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + }, + "hint": "_id_" + } + }, + { + "deleteOne": { + "filter": { + "_id": 2 + }, + "hint": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "BulkWrite_delete_hint", + "deletes": [ + { + "q": { + "_id": 1 + }, + "hint": "_id_", + "limit": 1 + }, + { + "q": { + "_id": 2 + }, + "hint": { + "_id": 1 + }, + "limit": 1 + } + ], + "ordered": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_delete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "BulkWrite deleteMany with hints unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": { + "_id": { + "$lt": 3 + } + }, + "hint": "_id_" + } + }, + { + "deleteMany": { + "filter": { + "_id": { + "$gte": 4 + } + }, + "hint": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "BulkWrite_delete_hint", + "deletes": [ + { + "q": { + "_id": { + "$lt": 3 + } + }, + "hint": "_id_", + "limit": 0 + }, + { + "q": { + "_id": { + "$gte": 4 + } + }, + "hint": { + "_id": 1 + }, + "limit": 0 + } + ], + "ordered": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_delete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint.json b/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint.json new file mode 100644 index 000000000..9fcdecefd --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint.json @@ -0,0 +1,247 @@ +{ + "description": "bulkWrite-delete-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.3.4" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "BulkWrite_delete_hint" + } + } + ], + "initialData": [ + { + "collectionName": "BulkWrite_delete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite deleteOne with hints", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + }, + "hint": "_id_" + } + }, + { + "deleteOne": { + "filter": { + "_id": 2 + }, + "hint": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 2, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "BulkWrite_delete_hint", + "deletes": [ + { + "q": { + "_id": 1 + }, + "hint": "_id_", + "limit": 1 + }, + { + "q": { + "_id": 2 + }, + "hint": { + "_id": 1 + }, + "limit": 1 + } + ], + "ordered": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_delete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "BulkWrite deleteMany with hints", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": { + "_id": { + "$lt": 3 + } + }, + "hint": "_id_" + } + }, + { + "deleteMany": { + "filter": { + "_id": { + "$gte": 4 + } + }, + "hint": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 3, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "BulkWrite_delete_hint", + "deletes": [ + { + "q": { + "_id": { + "$lt": 3 + } + }, + "hint": "_id_", + "limit": 0 + }, + { + "q": { + "_id": { + "$gte": 4 + } + }, + "hint": { + "_id": 1 + }, + "limit": 0 + } + ], + "ordered": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_delete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-hint-unacknowledged.json new file mode 100644 index 000000000..2dda9486e --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-hint-unacknowledged.json @@ -0,0 +1,269 @@ +{ + "description": "bulkWrite-deleteMany-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged deleteMany with hint string fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged deleteMany with hint document fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged deleteMany with hint string on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "limit": 0 + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged deleteMany with hint document on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "limit": 0 + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-let.json b/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-let.json new file mode 100644 index 000000000..45c20ea49 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-let.json @@ -0,0 +1,200 @@ +{ + "description": "BulkWrite deleteMany-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite deleteMany with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + } + } + } + ], + "let": { + "id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "limit": 0 + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "BulkWrite deleteMany with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + } + } + } + ], + "let": { + "id": 1 + } + }, + "expectError": { + "errorContains": "'delete.let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "limit": 1 + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-hint-unacknowledged.json new file mode 100644 index 000000000..aadf6d9e9 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-hint-unacknowledged.json @@ -0,0 +1,265 @@ +{ + "description": "bulkWrite-deleteOne-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged deleteOne with hint string fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged deleteOne with hint document fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged deleteOne with hint string on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "limit": 1 + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged deleteOne with hint document on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "limit": 1 + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-let.json b/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-let.json new file mode 100644 index 000000000..f3268163c --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-let.json @@ -0,0 +1,200 @@ +{ + "description": "BulkWrite deleteOne-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite deleteOne with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + } + } + } + ], + "let": { + "id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "limit": 1 + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "BulkWrite deleteOne with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.9" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + } + } + } + ], + "let": { + "id": 1 + } + }, + "expectError": { + "errorContains": "'delete.let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "limit": 1 + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-errorResponse.json b/tests/UnifiedSpecTests/crud/bulkWrite-errorResponse.json new file mode 100644 index 000000000..157637c71 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-errorResponse.json @@ -0,0 +1,88 @@ +{ + "description": "bulkWrite-errorResponse", + "schemaVersion": "1.12", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "bulkWrite operations support errorResponse assertions", + "runOnRequirements": [ + { + "minServerVersion": "4.0.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.2.0", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 8 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "errorCode": 8, + "errorResponse": { + "code": 8 + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-insertOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/bulkWrite-insertOne-dots_and_dollars.json new file mode 100644 index 000000000..92bbb1aaf --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-insertOne-dots_and_dollars.json @@ -0,0 +1,374 @@ +{ + "description": "bulkWrite-insertOne-dots_and_dollars", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Inserting document with top-level dollar-prefixed key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1, + "$a": 1 + } + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 1 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "$a": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "$a": 1 + } + ] + } + ] + }, + { + "description": "Inserting document with top-level dollar-prefixed key on pre-5.0 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "4.99" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1, + "$a": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "$a": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [] + } + ] + }, + { + "description": "Inserting document with top-level dotted key", + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1, + "a.b": 1 + } + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 1 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "a.b": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a.b": 1 + } + ] + } + ] + }, + { + "description": "Inserting document with dollar-prefixed key in embedded doc", + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1, + "a": { + "$b": 1 + } + } + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 1 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "a": { + "$b": 1 + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "$b": 1 + } + } + ] + } + ] + }, + { + "description": "Inserting document with dotted key in embedded doc", + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1, + "a": { + "b.c": 1 + } + } + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 1 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "a": { + "b.c": 1 + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "b.c": 1 + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-dots_and_dollars.json new file mode 100644 index 000000000..fce647d8f --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-dots_and_dollars.json @@ -0,0 +1,532 @@ +{ + "description": "bulkWrite-replaceOne-dots_and_dollars", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ], + "tests": [ + { + "description": "Replacing document with top-level dotted key on 3.6+ server", + "runOnRequirements": [ + { + "minServerVersion": "3.6" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a.b": 1 + } + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a.b": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a.b": 1 + } + ] + } + ] + }, + { + "description": "Replacing document with top-level dotted key on pre-3.6 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "3.4.99" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a.b": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a.b": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Replacing document with dollar-prefixed key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "$b": 1 + } + } + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a": { + "$b": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "$b": 1 + } + } + ] + } + ] + }, + { + "description": "Replacing document with dollar-prefixed key in embedded doc on pre-5.0 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "4.99" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "$b": 1 + } + } + } + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a": { + "$b": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Replacing document with dotted key in embedded doc on 3.6+ server", + "runOnRequirements": [ + { + "minServerVersion": "3.6" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "b.c": 1 + } + } + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a": { + "b.c": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "b.c": 1 + } + } + ] + } + ] + }, + { + "description": "Replacing document with dotted key in embedded doc on pre-3.6 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "3.4.99" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "b.c": 1 + } + } + } + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a": { + "b.c": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-hint-unacknowledged.json new file mode 100644 index 000000000..e54cd704d --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-hint-unacknowledged.json @@ -0,0 +1,293 @@ +{ + "description": "bulkWrite-replaceOne-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged replaceOne with hint string fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": "_id_" + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged replaceOne with hint document fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged replaceOne with hint string on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": "_id_" + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 111 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged replaceOne with hint document on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": { + "_id": 1 + } + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 111 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-let.json b/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-let.json new file mode 100644 index 000000000..70f63837a --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-let.json @@ -0,0 +1,226 @@ +{ + "description": "BulkWrite replaceOne-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite replaceOne with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "replacement": { + "x": 3 + } + } + } + ], + "let": { + "id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "u": { + "x": 3 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 3 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "BulkWrite replaceOne with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.9" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "replacement": { + "x": 3 + } + } + } + ], + "let": { + "id": 1 + } + }, + "expectError": { + "errorContains": "'update.let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "u": { + "x": 3 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/SpecTests/crud/bulkWrite-update-hint-clientError.json b/tests/UnifiedSpecTests/crud/bulkWrite-update-hint-clientError.json similarity index 63% rename from tests/SpecTests/crud/bulkWrite-update-hint-clientError.json rename to tests/UnifiedSpecTests/crud/bulkWrite-update-hint-clientError.json index fa919ec51..d5eb71c29 100644 --- a/tests/SpecTests/crud/bulkWrite-update-hint-clientError.json +++ b/tests/UnifiedSpecTests/crud/bulkWrite-update-hint-clientError.json @@ -1,39 +1,70 @@ { - "runOn": [ + "description": "bulkWrite-update-hint-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ { "maxServerVersion": "3.3.99" } ], - "data": [ + "createEntities": [ { - "_id": 1, - "x": 11 + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } }, { - "_id": 2, - "x": 22 + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } }, { - "_id": 3, - "x": 33 - }, + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_bulkwrite_update_hint" + } + } + ], + "initialData": [ { - "_id": 4, - "x": 44 + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] } ], - "collection_name": "test_bulkwrite_update_hint", "tests": [ { "description": "BulkWrite updateOne with update hints unsupported (client-side error)", "operations": [ { + "object": "collection0", "name": "bulkWrite", "arguments": { "requests": [ { - "name": "updateOne", - "arguments": { + "updateOne": { "filter": { "_id": 1 }, @@ -46,8 +77,7 @@ } }, { - "name": "updateOne", - "arguments": { + "updateOne": { "filter": { "_id": 1 }, @@ -62,17 +92,24 @@ } } ], - "options": { - "ordered": true - } + "ordered": true }, - "error": true + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] } ], - "expectations": [], - "outcome": { - "collection": { - "data": [ + "outcome": [ + { + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ { "_id": 1, "x": 11 @@ -91,18 +128,18 @@ } ] } - } + ] }, { "description": "BulkWrite updateMany with update hints unsupported (client-side error)", "operations": [ { + "object": "collection0", "name": "bulkWrite", "arguments": { "requests": [ { - "name": "updateMany", - "arguments": { + "updateMany": { "filter": { "_id": { "$lt": 3 @@ -117,8 +154,7 @@ } }, { - "name": "updateMany", - "arguments": { + "updateMany": { "filter": { "_id": { "$lt": 3 @@ -135,17 +171,24 @@ } } ], - "options": { - "ordered": true - } + "ordered": true }, - "error": true + "expectError": { + "isError": true + } } ], - "expectations": [], - "outcome": { - "collection": { - "data": [ + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ { "_id": 1, "x": 11 @@ -164,18 +207,18 @@ } ] } - } + ] }, { "description": "BulkWrite replaceOne with update hints unsupported (client-side error)", "operations": [ { + "object": "collection0", "name": "bulkWrite", "arguments": { "requests": [ { - "name": "replaceOne", - "arguments": { + "replaceOne": { "filter": { "_id": 3 }, @@ -186,8 +229,7 @@ } }, { - "name": "replaceOne", - "arguments": { + "replaceOne": { "filter": { "_id": 4 }, @@ -200,17 +242,24 @@ } } ], - "options": { - "ordered": true - } + "ordered": true }, - "error": true + "expectError": { + "isError": true + } } ], - "expectations": [], - "outcome": { - "collection": { - "data": [ + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ { "_id": 1, "x": 11 @@ -229,7 +278,7 @@ } ] } - } + ] } ] } diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-update-hint-serverError.json b/tests/UnifiedSpecTests/crud/bulkWrite-update-hint-serverError.json new file mode 100644 index 000000000..b0f7e1b38 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-update-hint-serverError.json @@ -0,0 +1,422 @@ +{ + "description": "bulkWrite-update-hint-serverError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.1.9" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_bulkwrite_update_hint" + } + } + ], + "initialData": [ + { + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite updateOne with update hints unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + } + }, + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_bulkwrite_update_hint", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_", + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": 1 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "BulkWrite updateMany with update hints unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "_id": { + "$lt": 3 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + } + }, + { + "updateMany": { + "filter": { + "_id": { + "$lt": 3 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_bulkwrite_update_hint", + "updates": [ + { + "q": { + "_id": { + "$lt": 3 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "hint": "_id_", + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$lt": 3 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "hint": { + "_id": 1 + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "BulkWrite replaceOne with update hints unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": 3 + }, + "replacement": { + "x": 333 + }, + "hint": "_id_" + } + }, + { + "replaceOne": { + "filter": { + "_id": 4 + }, + "replacement": { + "x": 444 + }, + "hint": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_bulkwrite_update_hint", + "updates": [ + { + "q": { + "_id": 3 + }, + "u": { + "x": 333 + }, + "hint": "_id_", + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": 4 + }, + "u": { + "x": 444 + }, + "hint": { + "_id": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-update-hint.json b/tests/UnifiedSpecTests/crud/bulkWrite-update-hint.json new file mode 100644 index 000000000..420635989 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-update-hint.json @@ -0,0 +1,445 @@ +{ + "description": "bulkWrite-update-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_bulkwrite_update_hint" + } + } + ], + "initialData": [ + { + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite updateOne with update hints", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + } + }, + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_bulkwrite_update_hint", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": "_id_" + }, + { + "q": { + "_id": 1 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "_id": 1 + } + } + ], + "ordered": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 13 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "BulkWrite updateMany with update hints", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "_id": { + "$lt": 3 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + } + }, + { + "updateMany": { + "filter": { + "_id": { + "$lt": 3 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 4, + "modifiedCount": 4, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_bulkwrite_update_hint", + "updates": [ + { + "q": { + "_id": { + "$lt": 3 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": "_id_" + }, + { + "q": { + "_id": { + "$lt": 3 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "_id": 1 + } + } + ], + "ordered": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 13 + }, + { + "_id": 2, + "x": 24 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "BulkWrite replaceOne with update hints", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": 3 + }, + "replacement": { + "x": 333 + }, + "hint": "_id_" + } + }, + { + "replaceOne": { + "filter": { + "_id": 4 + }, + "replacement": { + "x": 444 + }, + "hint": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_bulkwrite_update_hint", + "updates": [ + { + "q": { + "_id": 3 + }, + "u": { + "x": 333 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": "_id_" + }, + { + "q": { + "_id": 4 + }, + "u": { + "x": 444 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "_id": 1 + } + } + ], + "ordered": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_bulkwrite_update_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 333 + }, + { + "_id": 4, + "x": 444 + } + ] + } + ] + } + ] +} diff --git a/tests/SpecTests/crud/bulkWrite-update-validation.json b/tests/UnifiedSpecTests/crud/bulkWrite-update-validation.json similarity index 54% rename from tests/SpecTests/crud/bulkWrite-update-validation.json rename to tests/UnifiedSpecTests/crud/bulkWrite-update-validation.json index 481e13c45..f9bfda0ed 100644 --- a/tests/SpecTests/crud/bulkWrite-update-validation.json +++ b/tests/UnifiedSpecTests/crud/bulkWrite-update-validation.json @@ -1,16 +1,48 @@ { - "data": [ + "description": "bulkWrite-update-validation", + "schemaVersion": "1.0", + "createEntities": [ { - "_id": 1, - "x": 11 + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } }, { - "_id": 2, - "x": 22 + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } }, { - "_id": 3, - "x": 33 + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] } ], "tests": [ @@ -19,11 +51,11 @@ "operations": [ { "name": "bulkWrite", + "object": "collection0", "arguments": { "requests": [ { - "name": "replaceOne", - "arguments": { + "replaceOne": { "filter": { "_id": 1 }, @@ -36,13 +68,22 @@ } ] }, - "error": true + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] } ], - "expectations": [], - "outcome": { - "collection": { - "data": [ + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ { "_id": 1, "x": 11 @@ -57,18 +98,18 @@ } ] } - } + ] }, { "description": "BulkWrite updateOne requires atomic modifiers", "operations": [ { "name": "bulkWrite", + "object": "collection0", "arguments": { "requests": [ { - "name": "updateOne", - "arguments": { + "updateOne": { "filter": { "_id": 1 }, @@ -79,13 +120,22 @@ } ] }, - "error": true + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] } ], - "expectations": [], - "outcome": { - "collection": { - "data": [ + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ { "_id": 1, "x": 11 @@ -100,18 +150,18 @@ } ] } - } + ] }, { "description": "BulkWrite updateMany requires atomic modifiers", "operations": [ { "name": "bulkWrite", + "object": "collection0", "arguments": { "requests": [ { - "name": "updateMany", - "arguments": { + "updateMany": { "filter": { "_id": { "$gt": 1 @@ -124,13 +174,22 @@ } ] }, - "error": true + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] } ], - "expectations": [], - "outcome": { - "collection": { - "data": [ + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ { "_id": 1, "x": 11 @@ -145,7 +204,7 @@ } ] } - } + ] } ] } diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-dots_and_dollars.json new file mode 100644 index 000000000..35a5cdd52 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-dots_and_dollars.json @@ -0,0 +1,452 @@ +{ + "description": "bulkWrite-updateMany-dots_and_dollars", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {} + } + ] + } + ], + "tests": [ + { + "description": "Updating document to set top-level dollar-prefixed key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ] + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {}, + "$a": 1 + } + ] + } + ] + }, + { + "description": "Updating document to set top-level dotted key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ] + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {}, + "a.b": 1 + } + ] + } + ] + }, + { + "description": "Updating document to set dollar-prefixed key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ] + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": { + "$a": 1 + } + } + ] + } + ] + }, + { + "description": "Updating document to set dotted key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ] + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": { + "a.b": 1 + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-hint-unacknowledged.json new file mode 100644 index 000000000..87478918d --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-hint-unacknowledged.json @@ -0,0 +1,305 @@ +{ + "description": "bulkWrite-updateMany-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged updateMany with hint string fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged updateMany with hint document fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged updateMany with hint string on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged updateMany with hint document on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-let.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-let.json new file mode 100644 index 000000000..fbeba1a60 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-let.json @@ -0,0 +1,243 @@ +{ + "description": "BulkWrite updateMany-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 20 + }, + { + "_id": 2, + "x": 21 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite updateMany with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "update": [ + { + "$set": { + "x": 21 + } + } + ] + } + } + ], + "let": { + "id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "u": [ + { + "$set": { + "x": 21 + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 21 + }, + { + "_id": 2, + "x": 21 + } + ] + } + ] + }, + { + "description": "BulkWrite updateMany with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.9" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "update": [ + { + "$set": { + "x": 21 + } + } + ] + } + } + ], + "let": { + "id": 1 + } + }, + "expectError": { + "errorContains": "'update.let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "u": [ + { + "$set": { + "x": 21 + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 20 + }, + { + "_id": 2, + "x": 21 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-dots_and_dollars.json new file mode 100644 index 000000000..cbbe113ce --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-dots_and_dollars.json @@ -0,0 +1,460 @@ +{ + "description": "bulkWrite-updateOne-dots_and_dollars", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {} + } + ] + } + ], + "tests": [ + { + "description": "Updating document to set top-level dollar-prefixed key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ] + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {}, + "$a": 1 + } + ] + } + ] + }, + { + "description": "Updating document to set top-level dotted key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ] + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {}, + "a.b": 1 + } + ] + } + ] + }, + { + "description": "Updating document to set dollar-prefixed key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ] + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": { + "$a": 1 + } + } + ] + } + ] + }, + { + "description": "Updating document to set dotted key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ] + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": { + "a.b": 1 + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-hint-unacknowledged.json new file mode 100644 index 000000000..1345f6b53 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-hint-unacknowledged.json @@ -0,0 +1,305 @@ +{ + "description": "bulkWrite-updateOne-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged updateOne with hint string fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged updateOne with hint document fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged updateOne with hint string on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged updateOne with hint document on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-let.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-let.json new file mode 100644 index 000000000..96783c782 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-let.json @@ -0,0 +1,247 @@ +{ + "description": "BulkWrite updateOne-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 20 + }, + { + "_id": 2, + "x": 21 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite updateOne with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "update": [ + { + "$set": { + "x": 22 + } + } + ] + } + } + ], + "let": { + "id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "u": [ + { + "$set": { + "x": 22 + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + }, + { + "_id": 2, + "x": 21 + } + ] + } + ] + }, + { + "description": "BulkWrite updateOne with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.9" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "update": [ + { + "$set": { + "x": 22 + } + } + ] + } + } + ], + "let": { + "id": 1 + } + }, + "expectError": { + "errorContains": "'update.let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "u": [ + { + "$set": { + "x": 22 + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 20 + }, + { + "_id": 2, + "x": 21 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/countDocuments-comment.json b/tests/UnifiedSpecTests/crud/countDocuments-comment.json new file mode 100644 index 000000000..e6c7ae817 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/countDocuments-comment.json @@ -0,0 +1,208 @@ +{ + "description": "countDocuments-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "countDocuments-comments-test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "countDocuments-comments-test", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "countDocuments with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "filter": {}, + "comment": { + "key": "value" + } + }, + "expectResult": 3 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "comment": { + "key": "value" + } + }, + "commandName": "aggregate", + "databaseName": "countDocuments-comments-test" + } + } + ] + } + ] + }, + { + "description": "countDocuments with string comment", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0" + } + ], + "operations": [ + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "filter": {}, + "comment": "comment" + }, + "expectResult": 3 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "comment": "comment" + }, + "commandName": "aggregate", + "databaseName": "countDocuments-comments-test" + } + } + ] + } + ] + }, + { + "description": "countDocuments with document comment on less than 4.4.0 - server error", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.3.99" + } + ], + "operations": [ + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "filter": {}, + "comment": { + "key": "value" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "comment": { + "key": "value" + } + }, + "commandName": "aggregate", + "databaseName": "countDocuments-comments-test" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/db-aggregate-write-readPreference.json b/tests/UnifiedSpecTests/crud/db-aggregate-write-readPreference.json new file mode 100644 index 000000000..2a81282de --- /dev/null +++ b/tests/UnifiedSpecTests/crud/db-aggregate-write-readPreference.json @@ -0,0 +1,446 @@ +{ + "description": "db-aggregate-write-readPreference", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ], + "serverless": "forbid" + } + ], + "_yamlAnchors": { + "readConcern": { + "level": "local" + }, + "writeConcern": { + "w": 1 + } + }, + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "uriOptions": { + "readConcernLevel": "local", + "w": 1 + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0", + "databaseOptions": { + "readPreference": { + "mode": "secondaryPreferred", + "maxStalenessSeconds": 600 + } + } + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [] + } + ], + "tests": [ + { + "description": "Database-level aggregate with $out includes read preference for 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0", + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "database0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "_id": 1 + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$out": "coll0" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "_id": 1 + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$out": "coll0" + } + ], + "$readPreference": { + "mode": "secondaryPreferred", + "maxStalenessSeconds": 600 + }, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "w": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Database-level aggregate with $out omits read preference for pre-5.0 server", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.4.99", + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "database0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "_id": 1 + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$out": "coll0" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "_id": 1 + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$out": "coll0" + } + ], + "$readPreference": { + "$$exists": false + }, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "w": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Database-level aggregate with $merge includes read preference for 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "object": "database0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "_id": 1 + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$merge": { + "into": "coll0" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "_id": 1 + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$merge": { + "into": "coll0" + } + } + ], + "$readPreference": { + "mode": "secondaryPreferred", + "maxStalenessSeconds": 600 + }, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "w": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Database-level aggregate with $merge omits read preference for pre-5.0 server", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "object": "database0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "_id": 1 + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$merge": { + "into": "coll0" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "_id": 1 + } + }, + { + "$project": { + "_id": 1 + } + }, + { + "$merge": { + "into": "coll0" + } + } + ], + "$readPreference": { + "$$exists": false + }, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "w": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/tests/SpecTests/crud/db-aggregate.json b/tests/UnifiedSpecTests/crud/db-aggregate.json similarity index 68% rename from tests/SpecTests/crud/db-aggregate.json rename to tests/UnifiedSpecTests/crud/db-aggregate.json index d88b9e181..5015405bf 100644 --- a/tests/SpecTests/crud/db-aggregate.json +++ b/tests/UnifiedSpecTests/crud/db-aggregate.json @@ -1,17 +1,43 @@ { - "runOn": [ + "description": "db-aggregate", + "schemaVersion": "1.4", + "runOnRequirements": [ { - "minServerVersion": "3.6.0" + "minServerVersion": "3.6.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "admin" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "crud-v2" + } } ], - "database_name": "admin", "tests": [ { "description": "Aggregate with $listLocalSessions", "operations": [ { + "object": "database0", "name": "aggregate", - "object": "database", "arguments": { "pipeline": [ { @@ -33,7 +59,7 @@ } ] }, - "result": [ + "expectResult": [ { "dummy": "dummy field" } @@ -45,8 +71,8 @@ "description": "Aggregate with $listLocalSessions and allowDiskUse", "operations": [ { + "object": "database0", "name": "aggregate", - "object": "database", "arguments": { "pipeline": [ { @@ -69,7 +95,7 @@ ], "allowDiskUse": true }, - "result": [ + "expectResult": [ { "dummy": "dummy field" } diff --git a/tests/UnifiedSpecTests/crud/deleteMany-comment.json b/tests/UnifiedSpecTests/crud/deleteMany-comment.json new file mode 100644 index 000000000..6abc5fd58 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteMany-comment.json @@ -0,0 +1,245 @@ +{ + "description": "deleteMany-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name2" + }, + { + "_id": 3, + "name": "name3" + } + ] + } + ], + "tests": [ + { + "description": "deleteMany with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "comment": "comment" + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "limit": 0 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "deleteMany with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "comment": { + "key": "value" + } + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "limit": 0 + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "deleteMany with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "limit": 0 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name2" + }, + { + "_id": 3, + "name": "name3" + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteMany-hint-clientError.json b/tests/UnifiedSpecTests/crud/deleteMany-hint-clientError.json new file mode 100644 index 000000000..66320122b --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteMany-hint-clientError.json @@ -0,0 +1,149 @@ +{ + "description": "deleteMany-hint-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "maxServerVersion": "3.3.99" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "DeleteMany_hint" + } + } + ], + "initialData": [ + { + "collectionName": "DeleteMany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "DeleteMany with hint string unsupported (client-side error)", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "DeleteMany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "DeleteMany with hint document unsupported (client-side error)", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "DeleteMany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteMany-hint-serverError.json b/tests/UnifiedSpecTests/crud/deleteMany-hint-serverError.json new file mode 100644 index 000000000..88d4a6557 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteMany-hint-serverError.json @@ -0,0 +1,190 @@ +{ + "description": "deleteMany-hint-serverError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.3.3" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "DeleteMany_hint" + } + } + ], + "initialData": [ + { + "collectionName": "DeleteMany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "DeleteMany with hint string unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "DeleteMany_hint", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_", + "limit": 0 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "DeleteMany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "DeleteMany with hint document unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "DeleteMany_hint", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + }, + "limit": 0 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "DeleteMany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteMany-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/deleteMany-hint-unacknowledged.json new file mode 100644 index 000000000..ab7e9c7c0 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteMany-hint-unacknowledged.json @@ -0,0 +1,245 @@ +{ + "description": "deleteMany-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged deleteMany with hint string fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged deleteMany with hint document fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged deleteMany with hint string on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "limit": 0 + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged deleteMany with hint document on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "limit": 0 + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteMany-hint.json b/tests/UnifiedSpecTests/crud/deleteMany-hint.json new file mode 100644 index 000000000..59d903d20 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteMany-hint.json @@ -0,0 +1,173 @@ +{ + "description": "deleteMany-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.3.4" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "DeleteMany_hint" + } + } + ], + "initialData": [ + { + "collectionName": "DeleteMany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "DeleteMany with hint string", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "DeleteMany_hint", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_", + "limit": 0 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "DeleteMany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "DeleteMany with hint document", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "DeleteMany_hint", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + }, + "limit": 0 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "DeleteMany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteMany-let.json b/tests/UnifiedSpecTests/crud/deleteMany-let.json new file mode 100644 index 000000000..71bf26a01 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteMany-let.json @@ -0,0 +1,201 @@ +{ + "description": "deleteMany-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ], + "tests": [ + { + "description": "deleteMany with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$name", + "$$name" + ] + } + }, + "let": { + "name": "name" + } + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "$expr": { + "$eq": [ + "$name", + "$$name" + ] + } + }, + "limit": 0 + } + ], + "let": { + "name": "name" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "deleteMany with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$name", + "$$name" + ] + } + }, + "let": { + "name": "name" + } + }, + "expectError": { + "errorContains": "'delete.let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "$expr": { + "$eq": [ + "$name", + "$$name" + ] + } + }, + "limit": 0 + } + ], + "let": { + "name": "name" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-comment.json b/tests/UnifiedSpecTests/crud/deleteOne-comment.json new file mode 100644 index 000000000..0f42b086a --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteOne-comment.json @@ -0,0 +1,243 @@ +{ + "description": "deleteOne-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ], + "tests": [ + { + "description": "deleteOne with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": "comment" + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": 1 + }, + "limit": 1 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ] + }, + { + "description": "deleteOne with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": 1 + }, + "limit": 1 + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ] + }, + { + "description": "deleteOne with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": 1 + }, + "limit": 1 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-errorResponse.json b/tests/UnifiedSpecTests/crud/deleteOne-errorResponse.json new file mode 100644 index 000000000..1f3a266f1 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteOne-errorResponse.json @@ -0,0 +1,82 @@ +{ + "description": "deleteOne-errorResponse", + "schemaVersion": "1.12", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "delete operations support errorResponse assertions", + "runOnRequirements": [ + { + "minServerVersion": "4.0.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.2.0", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 8 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorCode": 8, + "errorResponse": { + "code": 8 + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-hint-clientError.json b/tests/UnifiedSpecTests/crud/deleteOne-hint-clientError.json new file mode 100644 index 000000000..cf629f59e --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteOne-hint-clientError.json @@ -0,0 +1,133 @@ +{ + "description": "deleteOne-hint-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "maxServerVersion": "3.3.99" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "DeleteOne_hint" + } + } + ], + "initialData": [ + { + "collectionName": "DeleteOne_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "DeleteOne with hint string unsupported (client-side error)", + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "DeleteOne_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "DeleteOne with hint document unsupported (client-side error)", + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "DeleteOne_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-hint-serverError.json b/tests/UnifiedSpecTests/crud/deleteOne-hint-serverError.json new file mode 100644 index 000000000..15541ed85 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteOne-hint-serverError.json @@ -0,0 +1,170 @@ +{ + "description": "deleteOne-hint-serverError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.3.3" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "DeleteOne_hint" + } + } + ], + "initialData": [ + { + "collectionName": "DeleteOne_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "DeleteOne with hint string unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "DeleteOne_hint", + "deletes": [ + { + "q": { + "_id": 1 + }, + "hint": "_id_", + "limit": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "DeleteOne_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "DeleteOne with hint document unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "DeleteOne_hint", + "deletes": [ + { + "q": { + "_id": 1 + }, + "hint": { + "_id": 1 + }, + "limit": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "DeleteOne_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/deleteOne-hint-unacknowledged.json new file mode 100644 index 000000000..1782f0f52 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteOne-hint-unacknowledged.json @@ -0,0 +1,241 @@ +{ + "description": "deleteOne-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged deleteOne with hint string fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged deleteOne with hint document fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged deleteOne with hint string on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "limit": 1 + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged deleteOne with hint document on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "limit": 1 + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-hint.json b/tests/UnifiedSpecTests/crud/deleteOne-hint.json new file mode 100644 index 000000000..bcc4bc234 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteOne-hint.json @@ -0,0 +1,161 @@ +{ + "description": "deleteOne-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.3.4" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "DeleteOne_hint" + } + } + ], + "initialData": [ + { + "collectionName": "DeleteOne_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "DeleteOne with hint string", + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": "_id_" + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "DeleteOne_hint", + "deletes": [ + { + "q": { + "_id": 1 + }, + "hint": "_id_", + "limit": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "DeleteOne_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "deleteOne with hint document", + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "DeleteOne_hint", + "deletes": [ + { + "q": { + "_id": 1 + }, + "hint": { + "_id": 1 + }, + "limit": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "DeleteOne_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-let.json b/tests/UnifiedSpecTests/crud/deleteOne-let.json new file mode 100644 index 000000000..971868223 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/deleteOne-let.json @@ -0,0 +1,191 @@ +{ + "description": "deleteOne-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "deleteOne with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "let": { + "id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "limit": 1 + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "deleteOne with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "let": { + "id": 1 + } + }, + "expectError": { + "errorContains": "'delete.let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "limit": 1 + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/distinct-comment.json b/tests/UnifiedSpecTests/crud/distinct-comment.json new file mode 100644 index 000000000..11bce9ac9 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/distinct-comment.json @@ -0,0 +1,186 @@ +{ + "description": "distinct-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "distinct-comment-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "distinct-comment-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "distinct with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4.14" + } + ], + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "comment": { + "key": "value" + } + }, + "expectResult": [ + 11, + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll0", + "key": "x", + "query": {}, + "comment": { + "key": "value" + } + }, + "commandName": "distinct", + "databaseName": "distinct-comment-tests" + } + } + ] + } + ] + }, + { + "description": "distinct with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "comment": "comment" + }, + "expectResult": [ + 11, + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll0", + "key": "x", + "query": {}, + "comment": "comment" + }, + "commandName": "distinct", + "databaseName": "distinct-comment-tests" + } + } + ] + } + ] + }, + { + "description": "distinct with document comment - pre 4.4, server error", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.4.13" + } + ], + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "comment": { + "key": "value" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll0", + "key": "x", + "query": {}, + "comment": { + "key": "value" + } + }, + "commandName": "distinct", + "databaseName": "distinct-comment-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/estimatedDocumentCount-comment.json b/tests/UnifiedSpecTests/crud/estimatedDocumentCount-comment.json new file mode 100644 index 000000000..6c0adacc8 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/estimatedDocumentCount-comment.json @@ -0,0 +1,170 @@ +{ + "description": "estimatedDocumentCount-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "edc-comment-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "edc-comment-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "estimatedDocumentCount with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4.14" + } + ], + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection0", + "arguments": { + "comment": { + "key": "value" + } + }, + "expectResult": 3 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll0", + "comment": { + "key": "value" + } + }, + "commandName": "count", + "databaseName": "edc-comment-tests" + } + } + ] + } + ] + }, + { + "description": "estimatedDocumentCount with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection0", + "arguments": { + "comment": "comment" + }, + "expectResult": 3 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll0", + "comment": "comment" + }, + "commandName": "count", + "databaseName": "edc-comment-tests" + } + } + ] + } + ] + }, + { + "description": "estimatedDocumentCount with document comment - pre 4.4.14, server error", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.4.13", + "topologies": [ + "single", + "replicaset" + ] + } + ], + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection0", + "arguments": { + "comment": { + "key": "value" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll0", + "comment": { + "key": "value" + } + }, + "commandName": "count", + "databaseName": "edc-comment-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/estimatedDocumentCount.json b/tests/UnifiedSpecTests/crud/estimatedDocumentCount.json new file mode 100644 index 000000000..1b650c1cb --- /dev/null +++ b/tests/UnifiedSpecTests/crud/estimatedDocumentCount.json @@ -0,0 +1,357 @@ +{ + "description": "estimatedDocumentCount", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "edc-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + }, + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "coll1" + } + }, + { + "collection": { + "id": "collection0View", + "database": "database0", + "collectionName": "coll0view" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "edc-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "estimatedDocumentCount always uses count", + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection0", + "expectResult": 3 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll0" + }, + "commandName": "count", + "databaseName": "edc-tests" + } + } + ] + } + ] + }, + { + "description": "estimatedDocumentCount with maxTimeMS", + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection0", + "arguments": { + "maxTimeMS": 6000 + }, + "expectResult": 3 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll0", + "maxTimeMS": 6000 + }, + "commandName": "count", + "databaseName": "edc-tests" + } + } + ] + } + ] + }, + { + "description": "estimatedDocumentCount on non-existent collection", + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection1", + "expectResult": 0 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll1" + }, + "commandName": "count", + "databaseName": "edc-tests" + } + } + ] + } + ] + }, + { + "description": "estimatedDocumentCount errors correctly--command error", + "runOnRequirements": [ + { + "minServerVersion": "4.0.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.2.0", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 8 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection0", + "expectError": { + "errorCode": 8 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll0" + }, + "commandName": "count", + "databaseName": "edc-tests" + } + } + ] + } + ] + }, + { + "description": "estimatedDocumentCount errors correctly--socket error", + "runOnRequirements": [ + { + "minServerVersion": "4.0.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.2.0", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection0", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll0" + }, + "commandName": "count", + "databaseName": "edc-tests" + } + } + ] + } + ] + }, + { + "description": "estimatedDocumentCount works correctly on views", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0" + } + ], + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "coll0view" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "collection": "coll0view", + "viewOn": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ] + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection0View", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "coll0view" + }, + "commandName": "drop", + "databaseName": "edc-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "coll0view", + "viewOn": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ] + }, + "commandName": "create", + "databaseName": "edc-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll0view" + }, + "commandName": "count", + "databaseName": "edc-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/find-allowdiskuse-clientError.json b/tests/UnifiedSpecTests/crud/find-allowdiskuse-clientError.json new file mode 100644 index 000000000..5bd954e79 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/find-allowdiskuse-clientError.json @@ -0,0 +1,79 @@ +{ + "description": "find-allowdiskuse-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "maxServerVersion": "3.0.99" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_find_allowdiskuse_clienterror" + } + } + ], + "tests": [ + { + "description": "Find fails when allowDiskUse true is specified against pre 3.2 server", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "allowDiskUse": true + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Find fails when allowDiskUse false is specified against pre 3.2 server", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "allowDiskUse": false + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/find-allowdiskuse-serverError.json b/tests/UnifiedSpecTests/crud/find-allowdiskuse-serverError.json new file mode 100644 index 000000000..dc58f8f0e --- /dev/null +++ b/tests/UnifiedSpecTests/crud/find-allowdiskuse-serverError.json @@ -0,0 +1,100 @@ +{ + "description": "find-allowdiskuse-serverError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.2", + "maxServerVersion": "4.3.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_find_allowdiskuse_servererror" + } + } + ], + "tests": [ + { + "description": "Find fails when allowDiskUse true is specified against pre 4.4 server (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "allowDiskUse": true + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test_find_allowdiskuse_servererror", + "filter": {}, + "allowDiskUse": true + } + } + } + ] + } + ] + }, + { + "description": "Find fails when allowDiskUse false is specified against pre 4.4 server (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "allowDiskUse": false + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test_find_allowdiskuse_servererror", + "filter": {}, + "allowDiskUse": false + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/find-allowdiskuse.json b/tests/UnifiedSpecTests/crud/find-allowdiskuse.json new file mode 100644 index 000000000..eb238ab93 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/find-allowdiskuse.json @@ -0,0 +1,120 @@ +{ + "description": "find-allowdiskuse", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_find_allowdiskuse" + } + } + ], + "tests": [ + { + "description": "Find does not send allowDiskUse when value is not specified", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test_find_allowdiskuse", + "allowDiskUse": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "Find sends allowDiskUse false when false is specified", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "allowDiskUse": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test_find_allowdiskuse", + "allowDiskUse": false + } + } + } + ] + } + ] + }, + { + "description": "Find sends allowDiskUse true when true is specified", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "allowDiskUse": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test_find_allowdiskuse", + "allowDiskUse": true + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/find-comment.json b/tests/UnifiedSpecTests/crud/find-comment.json new file mode 100644 index 000000000..600a3723f --- /dev/null +++ b/tests/UnifiedSpecTests/crud/find-comment.json @@ -0,0 +1,403 @@ +{ + "description": "find-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "tests": [ + { + "description": "find with string comment", + "runOnRequirements": [ + { + "minServerVersion": "3.6" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": "comment" + }, + "expectResult": [ + { + "_id": 1 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": 1 + }, + "comment": "comment" + } + } + } + ] + } + ] + }, + { + "description": "find with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + }, + "expectResult": [ + { + "_id": 1 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + } + } + } + ] + } + ] + }, + { + "description": "find with document comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99", + "minServerVersion": "3.6" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + } + } + } + ] + } + ] + }, + { + "description": "find with comment sets comment on getMore", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "batchSize": 2, + "comment": { + "key": "value" + } + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "batchSize": 2, + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "key": "value" + } + } + } + } + ] + } + ] + }, + { + "description": "find with comment does not set comment on getMore - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.3.99" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "batchSize": 2, + "comment": "comment" + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "batchSize": 2, + "comment": "comment" + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/find-let.json b/tests/UnifiedSpecTests/crud/find-let.json new file mode 100644 index 000000000..4e9c9c99f --- /dev/null +++ b/tests/UnifiedSpecTests/crud/find-let.json @@ -0,0 +1,148 @@ +{ + "description": "find-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Find with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "let": { + "id": 1 + } + }, + "expectResult": [ + { + "_id": 1 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "let": { + "id": 1 + } + } + } + } + ] + } + ] + }, + { + "description": "Find with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "let": { + "x": 1 + } + }, + "expectError": { + "errorContains": "Unrecognized field 'let'", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": 1 + }, + "let": { + "x": 1 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/find.json b/tests/UnifiedSpecTests/crud/find.json new file mode 100644 index 000000000..275d5d351 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/find.json @@ -0,0 +1,156 @@ +{ + "description": "find", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "find-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "find-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "tests": [ + { + "description": "find with multiple batches works", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "batchSize": 2 + }, + "object": "collection0", + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "batchSize": 2 + }, + "commandName": "find", + "databaseName": "find-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2 + }, + "commandName": "getMore", + "databaseName": "find-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2 + }, + "commandName": "getMore", + "databaseName": "find-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-comment.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-comment.json new file mode 100644 index 000000000..6853b9cc2 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndDelete-comment.json @@ -0,0 +1,211 @@ +{ + "description": "findOneAndDelete-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndDelete with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": "comment" + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "remove": true, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "findOneAndDelete with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "remove": true, + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "findOneAndDelete with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "remove": true, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-clientError.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-clientError.json new file mode 100644 index 000000000..c6ff46786 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-clientError.json @@ -0,0 +1,133 @@ +{ + "description": "findOneAndDelete-hint-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "findOneAndDelete_hint" + } + } + ], + "initialData": [ + { + "collectionName": "findOneAndDelete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndDelete with hint string unsupported (client-side error)", + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "findOneAndDelete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete with hint document", + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "findOneAndDelete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-serverError.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-serverError.json new file mode 100644 index 000000000..b87410272 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-serverError.json @@ -0,0 +1,162 @@ +{ + "description": "findOneAndDelete-hint-serverError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.3.3" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "findOneAndDelete_hint" + } + } + ], + "initialData": [ + { + "collectionName": "findOneAndDelete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndDelete with hint string unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndDelete_hint", + "query": { + "_id": 1 + }, + "hint": "_id_", + "remove": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndDelete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete with hint document unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndDelete_hint", + "query": { + "_id": 1 + }, + "hint": { + "_id": 1 + }, + "remove": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndDelete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-unacknowledged.json new file mode 100644 index 000000000..077f9892b --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-unacknowledged.json @@ -0,0 +1,225 @@ +{ + "description": "findOneAndDelete-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged findOneAndDelete with hint string fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged findOneAndDelete with hint document fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged findOneAndDelete with hint string on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": { + "$gt": 1 + } + }, + "remove": true, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged findOneAndDelete with hint document on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": { + "$gt": 1 + } + }, + "remove": true, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint.json new file mode 100644 index 000000000..8b53f2bd3 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint.json @@ -0,0 +1,155 @@ +{ + "description": "findOneAndDelete-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.3.4" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "findOneAndDelete_hint" + } + } + ], + "initialData": [ + { + "collectionName": "findOneAndDelete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndDelete with hint string", + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": "_id_" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndDelete_hint", + "query": { + "_id": 1 + }, + "hint": "_id_", + "remove": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndDelete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete with hint document", + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": 1 + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndDelete_hint", + "query": { + "_id": 1 + }, + "hint": { + "_id": 1 + }, + "remove": true + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndDelete_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-let.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-let.json new file mode 100644 index 000000000..ba8e681c0 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndDelete-let.json @@ -0,0 +1,180 @@ +{ + "description": "findOneAndDelete-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndDelete with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "let": { + "id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "remove": true, + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "findOneAndDelete with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "let": { + "id": 1 + } + }, + "expectError": { + "errorContains": "field 'let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "remove": true, + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-comment.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-comment.json new file mode 100644 index 000000000..f817bb693 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndReplace-comment.json @@ -0,0 +1,234 @@ +{ + "description": "findOneAndReplace-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndReplace with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 5 + }, + "comment": "comment" + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "x": 5 + }, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 5 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "findOneAndReplace with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 5 + }, + "comment": { + "key": "value" + } + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "x": 5 + }, + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 5 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "findOneAndReplace with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 5 + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "x": 5 + }, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-dots_and_dollars.json new file mode 100644 index 000000000..19ac447f8 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndReplace-dots_and_dollars.json @@ -0,0 +1,430 @@ +{ + "description": "findOneAndReplace-dots_and_dollars", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ], + "tests": [ + { + "description": "Replacing document with top-level dotted key on 3.6+ server", + "runOnRequirements": [ + { + "minServerVersion": "3.6" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a.b": 1 + } + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "_id": 1, + "a.b": 1 + }, + "new": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a.b": 1 + } + ] + } + ] + }, + { + "description": "Replacing document with top-level dotted key on pre-3.6 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "3.4.99" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a.b": 1 + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "_id": 1, + "a.b": 1 + }, + "new": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Replacing document with dollar-prefixed key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "$b": 1 + } + } + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "_id": 1, + "a": { + "$b": 1 + } + }, + "new": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "$b": 1 + } + } + ] + } + ] + }, + { + "description": "Replacing document with dollar-prefixed key in embedded doc on pre-5.0 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "4.99" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "$b": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "_id": 1, + "a": { + "$b": 1 + } + }, + "new": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Replacing document with dotted key in embedded doc on 3.6+ server", + "runOnRequirements": [ + { + "minServerVersion": "3.6" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "b.c": 1 + } + } + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "_id": 1, + "a": { + "b.c": 1 + } + }, + "new": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "b.c": 1 + } + } + ] + } + ] + }, + { + "description": "Replacing document with dotted key in embedded doc on pre-3.6 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "3.4.99" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "b.c": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "_id": 1, + "a": { + "b.c": 1 + } + }, + "new": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-clientError.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-clientError.json new file mode 100644 index 000000000..6b07eb1f4 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-clientError.json @@ -0,0 +1,139 @@ +{ + "description": "findOneAndReplace-hint-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "findOneAndReplace_hint" + } + } + ], + "initialData": [ + { + "collectionName": "findOneAndReplace_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndReplace with hint string unsupported (client-side error)", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 33 + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "findOneAndReplace_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace with hint document unsupported (client-side error)", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 33 + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "findOneAndReplace_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-serverError.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-serverError.json new file mode 100644 index 000000000..7fbf5a0ea --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-serverError.json @@ -0,0 +1,172 @@ +{ + "description": "findOneAndReplace-hint-serverError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.3.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "findOneAndReplace_hint" + } + } + ], + "initialData": [ + { + "collectionName": "findOneAndReplace_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndReplace with hint string unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 33 + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndReplace_hint", + "query": { + "_id": 1 + }, + "update": { + "x": 33 + }, + "hint": "_id_" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndReplace_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace with hint document unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 33 + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndReplace_hint", + "query": { + "_id": 1 + }, + "update": { + "x": 33 + }, + "hint": { + "_id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndReplace_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-unacknowledged.json new file mode 100644 index 000000000..8228d8a2a --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-unacknowledged.json @@ -0,0 +1,248 @@ +{ + "description": "findOneAndReplace-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + }, + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged findOneAndReplace with hint string fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": "_id_" + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged findOneAndReplace with hint document fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged findOneAndReplace with hint string on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": "_id_" + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": { + "$gt": 1 + } + }, + "update": { + "x": 111 + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged findOneAndReplace with hint document on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": { + "$gt": 1 + } + }, + "update": { + "x": 111 + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint.json new file mode 100644 index 000000000..d07c5921a --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint.json @@ -0,0 +1,173 @@ +{ + "description": "findOneAndReplace-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "findOneAndReplace_hint" + } + } + ], + "initialData": [ + { + "collectionName": "findOneAndReplace_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndReplace with hint string", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 33 + }, + "hint": "_id_" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndReplace_hint", + "query": { + "_id": 1 + }, + "update": { + "x": 33 + }, + "hint": "_id_" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndReplace_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 33 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace with hint document", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 33 + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndReplace_hint", + "query": { + "_id": 1 + }, + "update": { + "x": 33 + }, + "hint": { + "_id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndReplace_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 33 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-let.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-let.json new file mode 100644 index 000000000..5e5de44b3 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndReplace-let.json @@ -0,0 +1,197 @@ +{ + "description": "findOneAndReplace-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndReplace with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "replacement": { + "x": "x" + }, + "let": { + "id": 1 + } + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "update": { + "x": "x" + }, + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": "x" + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "findOneAndReplace with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "replacement": { + "x": "x" + }, + "let": { + "id": 1 + } + }, + "expectError": { + "errorContains": "field 'let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "update": { + "x": "x" + }, + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-comment.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-comment.json new file mode 100644 index 000000000..6dec5b39e --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndUpdate-comment.json @@ -0,0 +1,228 @@ +{ + "description": "findOneAndUpdate-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndUpdate with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + } + } + } + ] + } + ] + }, + { + "description": "findOneAndUpdate with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": { + "key": "value" + } + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ] + }, + { + "description": "findOneAndUpdate with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-dots_and_dollars.json new file mode 100644 index 000000000..40eb54739 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndUpdate-dots_and_dollars.json @@ -0,0 +1,380 @@ +{ + "description": "findOneAndUpdate-dots_and_dollars", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {} + } + ] + } + ], + "tests": [ + { + "description": "Updating document to set top-level dollar-prefixed key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ] + }, + "expectResult": { + "_id": 1, + "foo": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ], + "new": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {}, + "$a": 1 + } + ] + } + ] + }, + { + "description": "Updating document to set top-level dotted key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ] + }, + "expectResult": { + "_id": 1, + "foo": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ], + "new": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {}, + "a.b": 1 + } + ] + } + ] + }, + { + "description": "Updating document to set dollar-prefixed key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ] + }, + "expectResult": { + "_id": 1, + "foo": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ], + "new": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": { + "$a": 1 + } + } + ] + } + ] + }, + { + "description": "Updating document to set dotted key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ] + }, + "expectResult": { + "_id": 1, + "foo": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ], + "new": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": { + "a.b": 1 + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-errorResponse.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-errorResponse.json new file mode 100644 index 000000000..5023a450f --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndUpdate-errorResponse.json @@ -0,0 +1,132 @@ +{ + "description": "findOneAndUpdate-errorResponse", + "schemaVersion": "1.12", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": "foo" + } + ] + } + ], + "tests": [ + { + "description": "findOneAndUpdate DuplicateKey error is accessible", + "runOnRequirements": [ + { + "minServerVersion": "4.2" + } + ], + "operations": [ + { + "name": "createIndex", + "object": "collection0", + "arguments": { + "keys": { + "x": 1 + }, + "unique": true + } + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "update": { + "$set": { + "x": "foo" + } + }, + "upsert": true + }, + "expectError": { + "errorCode": 11000, + "errorResponse": { + "keyPattern": { + "x": 1 + }, + "keyValue": { + "x": "foo" + } + } + } + } + ] + }, + { + "description": "findOneAndUpdate document validation errInfo is accessible", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "modifyCollection", + "object": "database0", + "arguments": { + "collection": "test", + "validator": { + "x": { + "$type": "string" + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + }, + "expectError": { + "errorCode": 121, + "errorResponse": { + "errInfo": { + "failingDocumentId": 1, + "details": { + "$$type": "object" + } + } + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-clientError.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-clientError.json new file mode 100644 index 000000000..d0b51313c --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-clientError.json @@ -0,0 +1,143 @@ +{ + "description": "findOneAndUpdate-hint-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "findOneAndUpdate_hint" + } + } + ], + "initialData": [ + { + "collectionName": "findOneAndUpdate_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndUpdate with hint string unsupported (client-side error)", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "findOneAndUpdate_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate with hint document unsupported (client-side error)", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "findOneAndUpdate_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-serverError.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-serverError.json new file mode 100644 index 000000000..99fd9938f --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-serverError.json @@ -0,0 +1,180 @@ +{ + "description": "findOneAndUpdate-hint-serverError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.3.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "findOneAndUpdate_hint" + } + } + ], + "initialData": [ + { + "collectionName": "findOneAndUpdate_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndUpdate with hint string unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndUpdate_hint", + "query": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndUpdate_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate with hint document unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndUpdate_hint", + "query": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndUpdate_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-unacknowledged.json new file mode 100644 index 000000000..d116a06d0 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-unacknowledged.json @@ -0,0 +1,253 @@ +{ + "description": "findOneAndUpdate-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged findOneAndUpdate with hint string fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged findOneAndUpdate with hint document fails with client-side error on pre-4.4 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged findOneAndUpdate with hint string on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged findOneAndUpdate with hint document on 4.4+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": null + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "$$type": [ + "string", + "object" + ] + }, + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint.json new file mode 100644 index 000000000..5be6d2b3e --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint.json @@ -0,0 +1,181 @@ +{ + "description": "findOneAndUpdate-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "findOneAndUpdate_hint" + } + } + ], + "initialData": [ + { + "collectionName": "findOneAndUpdate_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndUpdate with hint string", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndUpdate_hint", + "query": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndUpdate_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate with hint document", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "findOneAndUpdate_hint", + "query": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "findOneAndUpdate_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-let.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-let.json new file mode 100644 index 000000000..74d7d0e58 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/findOneAndUpdate-let.json @@ -0,0 +1,217 @@ +{ + "description": "findOneAndUpdate-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndUpdate with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "update": [ + { + "$set": { + "x": "$$x" + } + } + ], + "let": { + "id": 1, + "x": "foo" + } + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "update": [ + { + "$set": { + "x": "$$x" + } + } + ], + "let": { + "id": 1, + "x": "foo" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": "foo" + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "findOneAndUpdate with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "update": [ + { + "$set": { + "x": "$$x" + } + } + ], + "let": { + "id": 1, + "x": "foo" + } + }, + "expectError": { + "errorContains": "field 'let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "update": [ + { + "$set": { + "x": "$$x" + } + } + ], + "let": { + "id": 1, + "x": "foo" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/insertMany-comment.json b/tests/UnifiedSpecTests/crud/insertMany-comment.json new file mode 100644 index 000000000..2b4c80b3f --- /dev/null +++ b/tests/UnifiedSpecTests/crud/insertMany-comment.json @@ -0,0 +1,226 @@ +{ + "description": "insertMany-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "insertMany with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "insertMany with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": { + "key": "value" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "insertMany with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/insertMany-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/insertMany-dots_and_dollars.json new file mode 100644 index 000000000..eed8997df --- /dev/null +++ b/tests/UnifiedSpecTests/crud/insertMany-dots_and_dollars.json @@ -0,0 +1,338 @@ +{ + "description": "insertMany-dots_and_dollars", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Inserting document with top-level dollar-prefixed key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 1, + "$a": 1 + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 1 + } + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "$a": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "$a": 1 + } + ] + } + ] + }, + { + "description": "Inserting document with top-level dollar-prefixed key on pre-5.0 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "4.99" + } + ], + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 1, + "$a": 1 + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "$a": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [] + } + ] + }, + { + "description": "Inserting document with top-level dotted key", + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 1, + "a.b": 1 + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 1 + } + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "a.b": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a.b": 1 + } + ] + } + ] + }, + { + "description": "Inserting document with dollar-prefixed key in embedded doc", + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 1, + "a": { + "$b": 1 + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 1 + } + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "a": { + "$b": 1 + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "$b": 1 + } + } + ] + } + ] + }, + { + "description": "Inserting document with dotted key in embedded doc", + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 1, + "a": { + "b.c": 1 + } + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 1 + } + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "a": { + "b.c": 1 + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "b.c": 1 + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/insertOne-comment.json b/tests/UnifiedSpecTests/crud/insertOne-comment.json new file mode 100644 index 000000000..dbd83d9f6 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/insertOne-comment.json @@ -0,0 +1,220 @@ +{ + "description": "insertOne-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "insertOne with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 2, + "x": 22 + }, + "comment": "comment" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "insertOne with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 2, + "x": 22 + }, + "comment": { + "key": "value" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "insertOne with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 2, + "x": 22 + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/insertOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/insertOne-dots_and_dollars.json new file mode 100644 index 000000000..fdc17af2e --- /dev/null +++ b/tests/UnifiedSpecTests/crud/insertOne-dots_and_dollars.json @@ -0,0 +1,614 @@ +{ + "description": "insertOne-dots_and_dollars", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + }, + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "coll1", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Inserting document with top-level dollar-prefixed key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "$a": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "$a": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "$a": 1 + } + ] + } + ] + }, + { + "description": "Inserting document with top-level dollar-prefixed key on pre-5.0 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "4.99" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "$a": 1 + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "$a": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [] + } + ] + }, + { + "description": "Inserting document with top-level dotted key", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a.b": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "a.b": 1 + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a.b": 1 + } + ] + } + ] + }, + { + "description": "Inserting document with dollar-prefixed key in embedded doc", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": { + "$b": 1 + } + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "a": { + "$b": 1 + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "$b": 1 + } + } + ] + } + ] + }, + { + "description": "Inserting document with dotted key in embedded doc", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": { + "b.c": 1 + } + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "a": { + "b.c": 1 + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "b.c": 1 + } + } + ] + } + ] + }, + { + "description": "Inserting document with dollar-prefixed key in _id yields server-side error", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": { + "$a": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": { + "$a": 1 + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [] + } + ] + }, + { + "description": "Inserting document with dotted key in _id on 3.6+ server", + "runOnRequirements": [ + { + "minServerVersion": "3.6" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": { + "a.b": 1 + } + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": { + "a.b": 1 + } + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": { + "a.b": 1 + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": { + "a.b": 1 + } + } + ] + } + ] + }, + { + "description": "Inserting document with dotted key in _id on pre-3.6 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "3.4.99" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": { + "a.b": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": { + "a.b": 1 + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [] + } + ] + }, + { + "description": "Inserting document with DBRef-like keys", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": { + "$db": "foo" + } + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1, + "a": { + "$db": "foo" + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "$db": "foo" + } + } + ] + } + ] + }, + { + "description": "Unacknowledged write using dollar-prefixed or dotted keys may be silently rejected on pre-5.0 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.99" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection1", + "arguments": { + "document": { + "_id": { + "$a": 1 + } + } + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll1", + "documents": [ + { + "_id": { + "$a": 1 + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/insertOne-errorResponse.json b/tests/UnifiedSpecTests/crud/insertOne-errorResponse.json new file mode 100644 index 000000000..04ea6a745 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/insertOne-errorResponse.json @@ -0,0 +1,82 @@ +{ + "description": "insertOne-errorResponse", + "schemaVersion": "1.12", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "insert operations support errorResponse assertions", + "runOnRequirements": [ + { + "minServerVersion": "4.0.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.2.0", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 8 + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + }, + "expectError": { + "errorCode": 8, + "errorResponse": { + "code": 8 + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-comment.json b/tests/UnifiedSpecTests/crud/replaceOne-comment.json new file mode 100644 index 000000000..88bee5d7b --- /dev/null +++ b/tests/UnifiedSpecTests/crud/replaceOne-comment.json @@ -0,0 +1,248 @@ +{ + "description": "replaceOne-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "ReplaceOne with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 22 + }, + "comment": "comment" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "x": 22 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 22 + }, + "comment": { + "key": "value" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "x": 22 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 22 + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "x": 22 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/replaceOne-dots_and_dollars.json new file mode 100644 index 000000000..d5003dc5e --- /dev/null +++ b/tests/UnifiedSpecTests/crud/replaceOne-dots_and_dollars.json @@ -0,0 +1,567 @@ +{ + "description": "replaceOne-dots_and_dollars", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + }, + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "coll1", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ], + "tests": [ + { + "description": "Replacing document with top-level dotted key on 3.6+ server", + "runOnRequirements": [ + { + "minServerVersion": "3.6" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a.b": 1 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a.b": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a.b": 1 + } + ] + } + ] + }, + { + "description": "Replacing document with top-level dotted key on pre-3.6 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "3.4.99" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a.b": 1 + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a.b": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Replacing document with dollar-prefixed key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "$b": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a": { + "$b": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "$b": 1 + } + } + ] + } + ] + }, + { + "description": "Replacing document with dollar-prefixed key in embedded doc on pre-5.0 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "4.99" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "$b": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a": { + "$b": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Replacing document with dotted key in embedded doc on 3.6+ server", + "runOnRequirements": [ + { + "minServerVersion": "3.6" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "b.c": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a": { + "b.c": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "a": { + "b.c": 1 + } + } + ] + } + ] + }, + { + "description": "Replacing document with dotted key in embedded doc on pre-3.6 server yields server-side error", + "runOnRequirements": [ + { + "maxServerVersion": "3.4.99" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "b.c": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a": { + "b.c": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Unacknowledged write using dollar-prefixed or dotted keys may be silently rejected on pre-5.0 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.99" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection1", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "a": { + "$b": 1 + } + } + }, + "expectResult": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll1", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "a": { + "$b": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/replaceOne-hint-unacknowledged.json new file mode 100644 index 000000000..5c5dec64f --- /dev/null +++ b/tests/UnifiedSpecTests/crud/replaceOne-hint-unacknowledged.json @@ -0,0 +1,269 @@ +{ + "description": "replaceOne-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged replaceOne with hint string fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": "_id_" + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged replaceOne with hint document fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged replaceOne with hint string on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": "_id_" + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 111 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged replaceOne with hint document on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 111 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-hint.json b/tests/UnifiedSpecTests/crud/replaceOne-hint.json new file mode 100644 index 000000000..6926e9d8d --- /dev/null +++ b/tests/UnifiedSpecTests/crud/replaceOne-hint.json @@ -0,0 +1,203 @@ +{ + "description": "replaceOne-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_replaceone_hint" + } + } + ], + "initialData": [ + { + "collectionName": "test_replaceone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "ReplaceOne with hint string", + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": "_id_" + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_replaceone_hint", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 111 + }, + "hint": "_id_", + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_replaceone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 111 + } + ] + } + ] + }, + { + "description": "ReplaceOne with hint document", + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_replaceone_hint", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 111 + }, + "hint": { + "_id": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_replaceone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 111 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-let.json b/tests/UnifiedSpecTests/crud/replaceOne-let.json new file mode 100644 index 000000000..e7a7ee65a --- /dev/null +++ b/tests/UnifiedSpecTests/crud/replaceOne-let.json @@ -0,0 +1,219 @@ +{ + "description": "replaceOne-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "ReplaceOne with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "replacement": { + "x": "foo" + }, + "let": { + "id": 1 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "u": { + "x": "foo" + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": "foo" + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "ReplaceOne with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "replacement": { + "x": "foo" + }, + "let": { + "id": 1 + } + }, + "expectError": { + "errorContains": "'update.let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "u": { + "x": "foo" + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-validation.json b/tests/UnifiedSpecTests/crud/replaceOne-validation.json new file mode 100644 index 000000000..6f5b173e0 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/replaceOne-validation.json @@ -0,0 +1,82 @@ +{ + "description": "replaceOne-validation", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "ReplaceOne prohibits atomic modifiers", + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "$set": { + "x": 22 + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateMany-comment.json b/tests/UnifiedSpecTests/crud/updateMany-comment.json new file mode 100644 index 000000000..88b8b67f5 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateMany-comment.json @@ -0,0 +1,254 @@ +{ + "description": "updateMany-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "UpdateMany with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": "comment" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateMany with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": { + "key": "value" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateMany with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateMany-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/updateMany-dots_and_dollars.json new file mode 100644 index 000000000..5d3b9d045 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateMany-dots_and_dollars.json @@ -0,0 +1,404 @@ +{ + "description": "updateMany-dots_and_dollars", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {} + } + ] + } + ], + "tests": [ + { + "description": "Updating document to set top-level dollar-prefixed key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {}, + "$a": 1 + } + ] + } + ] + }, + { + "description": "Updating document to set top-level dotted key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {}, + "a.b": 1 + } + ] + } + ] + }, + { + "description": "Updating document to set dollar-prefixed key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": { + "$a": 1 + } + } + ] + } + ] + }, + { + "description": "Updating document to set dotted key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": { + "a.b": 1 + } + } + ] + } + ] + } + ] +} diff --git a/tests/SpecTests/crud/updateMany-hint-clientError.json b/tests/UnifiedSpecTests/crud/updateMany-hint-clientError.json similarity index 50% rename from tests/SpecTests/crud/updateMany-hint-clientError.json rename to tests/UnifiedSpecTests/crud/updateMany-hint-clientError.json index 44ebddc53..5da878e29 100644 --- a/tests/SpecTests/crud/updateMany-hint-clientError.json +++ b/tests/UnifiedSpecTests/crud/updateMany-hint-clientError.json @@ -1,30 +1,61 @@ { - "runOn": [ + "description": "updateMany-hint-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ { "maxServerVersion": "3.3.99" } ], - "data": [ + "createEntities": [ { - "_id": 1, - "x": 11 + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } }, { - "_id": 2, - "x": 22 + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } }, { - "_id": 3, - "x": 33 + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_updatemany_hint" + } + } + ], + "initialData": [ + { + "collectionName": "test_updatemany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] } ], - "collection_name": "test_updatemany_hint", "tests": [ { "description": "UpdateMany with hint string unsupported (client-side error)", "operations": [ { - "object": "collection", + "object": "collection0", "name": "updateMany", "arguments": { "filter": { @@ -39,13 +70,22 @@ }, "hint": "_id_" }, - "error": true + "expectError": { + "isError": true + } } ], - "expectations": [], - "outcome": { - "collection": { - "data": [ + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "test_updatemany_hint", + "databaseName": "crud-v2", + "documents": [ { "_id": 1, "x": 11 @@ -60,13 +100,13 @@ } ] } - } + ] }, { "description": "UpdateMany with hint document unsupported (client-side error)", "operations": [ { - "object": "collection", + "object": "collection0", "name": "updateMany", "arguments": { "filter": { @@ -83,13 +123,22 @@ "_id": 1 } }, - "error": true + "expectError": { + "isError": true + } } ], - "expectations": [], - "outcome": { - "collection": { - "data": [ + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "test_updatemany_hint", + "databaseName": "crud-v2", + "documents": [ { "_id": 1, "x": 11 @@ -104,7 +153,7 @@ } ] } - } + ] } ] } diff --git a/tests/UnifiedSpecTests/crud/updateMany-hint-serverError.json b/tests/UnifiedSpecTests/crud/updateMany-hint-serverError.json new file mode 100644 index 000000000..c81f36b13 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateMany-hint-serverError.json @@ -0,0 +1,216 @@ +{ + "description": "updateMany-hint-serverError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.1.9" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_updatemany_hint" + } + } + ], + "initialData": [ + { + "collectionName": "test_updatemany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "UpdateMany with hint string unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_updatemany_hint", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "hint": "_id_", + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_updatemany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "UpdateMany with hint document unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_updatemany_hint", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "hint": { + "_id": 1 + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_updatemany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateMany-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/updateMany-hint-unacknowledged.json new file mode 100644 index 000000000..e83838aac --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateMany-hint-unacknowledged.json @@ -0,0 +1,281 @@ +{ + "description": "updateMany-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged updateMany with hint string fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged updateMany with hint document fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged updateMany with hint string on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged updateMany with hint document on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateMany-hint.json b/tests/UnifiedSpecTests/crud/updateMany-hint.json new file mode 100644 index 000000000..929be5299 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateMany-hint.json @@ -0,0 +1,219 @@ +{ + "description": "updateMany-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_updatemany_hint" + } + } + ], + "initialData": [ + { + "collectionName": "test_updatemany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "UpdateMany with hint string", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_updatemany_hint", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "hint": "_id_", + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_updatemany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 34 + } + ] + } + ] + }, + { + "description": "UpdateMany with hint document", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_updatemany_hint", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "hint": { + "_id": 1 + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_updatemany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 34 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateMany-let.json b/tests/UnifiedSpecTests/crud/updateMany-let.json new file mode 100644 index 000000000..cff3bd4c7 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateMany-let.json @@ -0,0 +1,249 @@ +{ + "description": "updateMany-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ], + "tests": [ + { + "description": "updateMany with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$name", + "$$name" + ] + } + }, + "update": [ + { + "$set": { + "x": "$$x", + "y": "$$y" + } + } + ], + "let": { + "name": "name", + "x": "foo", + "y": { + "$literal": "bar" + } + } + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "$expr": { + "$eq": [ + "$name", + "$$name" + ] + } + }, + "u": [ + { + "$set": { + "x": "$$x", + "y": "$$y" + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "name": "name", + "x": "foo", + "y": { + "$literal": "bar" + } + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name", + "x": "foo", + "y": "bar" + }, + { + "_id": 3, + "name": "name", + "x": "foo", + "y": "bar" + } + ] + } + ] + }, + { + "description": "updateMany with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": "$$x" + } + } + ], + "let": { + "x": "foo" + } + }, + "expectError": { + "errorContains": "'update.let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$set": { + "x": "$$x" + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "x": "foo" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateMany-validation.json b/tests/UnifiedSpecTests/crud/updateMany-validation.json new file mode 100644 index 000000000..e3e46a138 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateMany-validation.json @@ -0,0 +1,98 @@ +{ + "description": "updateMany-validation", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "UpdateMany requires atomic modifiers", + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "x": 44 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateOne-comment.json b/tests/UnifiedSpecTests/crud/updateOne-comment.json new file mode 100644 index 000000000..f4ee74db3 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateOne-comment.json @@ -0,0 +1,260 @@ +{ + "description": "updateOne-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": "comment" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": { + "key": "value" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/updateOne-dots_and_dollars.json new file mode 100644 index 000000000..798d522cb --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateOne-dots_and_dollars.json @@ -0,0 +1,412 @@ +{ + "description": "updateOne-dots_and_dollars", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {} + } + ] + } + ], + "tests": [ + { + "description": "Updating document to set top-level dollar-prefixed key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {}, + "$a": 1 + } + ] + } + ] + }, + { + "description": "Updating document to set top-level dotted key on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$replaceWith": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$$ROOT" + } + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": {}, + "a.b": 1 + } + ] + } + ] + }, + { + "description": "Updating document to set dollar-prefixed key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "$a" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": { + "$a": 1 + } + } + ] + } + ] + }, + { + "description": "Updating document to set dotted key in embedded doc on 5.0+ server", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$set": { + "foo": { + "$setField": { + "field": { + "$literal": "a.b" + }, + "value": 1, + "input": "$foo" + } + } + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "foo": { + "a.b": 1 + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateOne-errorResponse.json b/tests/UnifiedSpecTests/crud/updateOne-errorResponse.json new file mode 100644 index 000000000..0ceddbc4f --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateOne-errorResponse.json @@ -0,0 +1,87 @@ +{ + "description": "updateOne-errorResponse", + "schemaVersion": "1.12", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "update operations support errorResponse assertions", + "runOnRequirements": [ + { + "minServerVersion": "4.0.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.2.0", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 8 + } + } + } + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + }, + "expectError": { + "errorCode": 8, + "errorResponse": { + "code": 8 + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateOne-hint-clientError.json b/tests/UnifiedSpecTests/crud/updateOne-hint-clientError.json new file mode 100644 index 000000000..d4f1a5343 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateOne-hint-clientError.json @@ -0,0 +1,147 @@ +{ + "description": "updateOne-hint-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "maxServerVersion": "3.3.99" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_updateone_hint" + } + } + ], + "initialData": [ + { + "collectionName": "test_updateone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne with hint string unsupported (client-side error)", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "test_updateone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne with hint document unsupported (client-side error)", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "test_updateone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateOne-hint-serverError.json b/tests/UnifiedSpecTests/crud/updateOne-hint-serverError.json new file mode 100644 index 000000000..05fb03331 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateOne-hint-serverError.json @@ -0,0 +1,208 @@ +{ + "description": "updateOne-hint-serverError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.4.0", + "maxServerVersion": "4.1.9" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_updateone_hint" + } + } + ], + "initialData": [ + { + "collectionName": "test_updateone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne with hint string unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_updateone_hint", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_", + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_updateone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne with hint document unsupported (server-side error)", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_updateone_hint", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_updateone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/updateOne-hint-unacknowledged.json new file mode 100644 index 000000000..859b0f92f --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateOne-hint-unacknowledged.json @@ -0,0 +1,281 @@ +{ + "description": "updateOne-hint-unacknowledged", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "db0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Unacknowledged updateOne with hint string fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged updateOne with hint document fails with client-side error on pre-4.2 server", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Unacknowledged updateOne with hint string on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + }, + { + "description": "Unacknowledged updateOne with hint document on 4.2+ server", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + }, + "hint": { + "$$type": [ + "string", + "object" + ] + } + } + ], + "writeConcern": { + "w": 0 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateOne-hint.json b/tests/UnifiedSpecTests/crud/updateOne-hint.json new file mode 100644 index 000000000..484e00757 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateOne-hint.json @@ -0,0 +1,211 @@ +{ + "description": "updateOne-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test_updateone_hint" + } + } + ], + "initialData": [ + { + "collectionName": "test_updateone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne with hint string", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_" + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_updateone_hint", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "hint": "_id_", + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_updateone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 23 + } + ] + } + ] + }, + { + "description": "UpdateOne with hint document", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test_updateone_hint", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "hint": { + "_id": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test_updateone_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 23 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateOne-let.json b/tests/UnifiedSpecTests/crud/updateOne-let.json new file mode 100644 index 000000000..e43b97935 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateOne-let.json @@ -0,0 +1,227 @@ +{ + "description": "updateOne-let", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne with let option", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "update": [ + { + "$set": { + "x": "$$x" + } + } + ], + "let": { + "id": 1, + "x": "foo" + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "$expr": { + "$eq": [ + "$_id", + "$$id" + ] + } + }, + "u": [ + { + "$set": { + "x": "$$x" + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "id": 1, + "x": "foo" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": "foo" + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "UpdateOne with let option unsupported (server-side error)", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.4.99" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": "$$x" + } + } + ], + "let": { + "x": "foo" + } + }, + "expectError": { + "errorContains": "'update.let' is an unknown field", + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$set": { + "x": "$$x" + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "let": { + "x": "foo" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateOne-validation.json b/tests/UnifiedSpecTests/crud/updateOne-validation.json new file mode 100644 index 000000000..1464642c5 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateOne-validation.json @@ -0,0 +1,80 @@ +{ + "description": "updateOne-validation", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne requires atomic modifiers", + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "x": 22 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/crud/updateWithPipelines.json b/tests/UnifiedSpecTests/crud/updateWithPipelines.json new file mode 100644 index 000000000..164f2f6a1 --- /dev/null +++ b/tests/UnifiedSpecTests/crud/updateWithPipelines.json @@ -0,0 +1,494 @@ +{ + "description": "updateWithPipelines", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.1.11" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 1, + "y": 1, + "t": { + "u": { + "v": 1 + } + } + }, + { + "_id": 2, + "x": 2, + "y": 1 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne using pipelines", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceRoot": { + "newRoot": "$t" + } + }, + { + "$addFields": { + "foo": 1 + } + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$replaceRoot": { + "newRoot": "$t" + } + }, + { + "$addFields": { + "foo": 1 + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + }, + "commandName": "update", + "databaseName": "crud-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "u": { + "v": 1 + }, + "foo": 1 + }, + { + "_id": 2, + "x": 2, + "y": 1 + } + ] + } + ] + }, + { + "description": "UpdateMany using pipelines", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": {}, + "update": [ + { + "$project": { + "x": 1 + } + }, + { + "$addFields": { + "foo": 1 + } + } + ] + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": {}, + "u": [ + { + "$project": { + "x": 1 + } + }, + { + "$addFields": { + "foo": 1 + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + }, + "commandName": "update", + "databaseName": "crud-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 1, + "foo": 1 + }, + { + "_id": 2, + "x": 2, + "foo": 1 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate using pipelines", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$project": { + "x": 1 + } + }, + { + "$addFields": { + "foo": 1 + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "update": [ + { + "$project": { + "x": 1 + } + }, + { + "$addFields": { + "foo": 1 + } + } + ] + }, + "commandName": "findAndModify", + "databaseName": "crud-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 1, + "foo": 1 + }, + { + "_id": 2, + "x": 2, + "y": 1 + } + ] + } + ] + }, + { + "description": "UpdateOne in bulk write using pipelines", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$replaceRoot": { + "newRoot": "$t" + } + }, + { + "$addFields": { + "foo": 1 + } + } + ] + } + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": [ + { + "$replaceRoot": { + "newRoot": "$t" + } + }, + { + "$addFields": { + "foo": 1 + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + }, + "commandName": "update", + "databaseName": "crud-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "u": { + "v": 1 + }, + "foo": 1 + }, + { + "_id": 2, + "x": 2, + "y": 1 + } + ] + } + ] + }, + { + "description": "UpdateMany in bulk write using pipelines", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": {}, + "update": [ + { + "$project": { + "x": 1 + } + }, + { + "$addFields": { + "foo": 1 + } + } + ] + } + } + ] + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": {}, + "u": [ + { + "$project": { + "x": 1 + } + }, + { + "$addFields": { + "foo": 1 + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + }, + "commandName": "update", + "databaseName": "crud-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 1, + "foo": 1 + }, + { + "_id": 2, + "x": 2, + "foo": 1 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/gridfs/delete.json b/tests/UnifiedSpecTests/gridfs/delete.json new file mode 100644 index 000000000..7a4ec27f8 --- /dev/null +++ b/tests/UnifiedSpecTests/gridfs/delete.json @@ -0,0 +1,799 @@ +{ + "description": "gridfs-delete", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "gridfs-tests" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0" + } + }, + { + "collection": { + "id": "bucket0_files_collection", + "database": "database0", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "bucket0_chunks_collection", + "database": "database0", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0-with-empty-chunk", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "c700ed4fdb1d27055aa3faa2c2432283", + "filename": "length-2", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "dd254cdc958e53abaa67da9f797125f5", + "filename": "length-8", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VWZ3iA==", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "delete when length is 0", + "operations": [ + { + "name": "delete", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + } + } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0-with-empty-chunk", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "c700ed4fdb1d27055aa3faa2c2432283", + "filename": "length-2", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "dd254cdc958e53abaa67da9f797125f5", + "filename": "length-8", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VWZ3iA==", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "delete when length is 0 and there is one extra empty chunk", + "operations": [ + { + "name": "delete", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000002" + } + } + } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "c700ed4fdb1d27055aa3faa2c2432283", + "filename": "length-2", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "dd254cdc958e53abaa67da9f797125f5", + "filename": "length-8", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VWZ3iA==", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "delete when length is 8", + "operations": [ + { + "name": "delete", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000004" + } + } + } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0-with-empty-chunk", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "c700ed4fdb1d27055aa3faa2c2432283", + "filename": "length-2", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "delete when files entry does not exist", + "operations": [ + { + "name": "delete", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000000" + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0-with-empty-chunk", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "c700ed4fdb1d27055aa3faa2c2432283", + "filename": "length-2", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "dd254cdc958e53abaa67da9f797125f5", + "filename": "length-8", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VWZ3iA==", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "delete when files entry does not exist and there are orphaned chunks", + "operations": [ + { + "name": "deleteOne", + "object": "bucket0_files_collection", + "arguments": { + "filter": { + "_id": { + "$oid": "000000000000000000000004" + } + } + }, + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "delete", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000004" + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0-with-empty-chunk", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "c700ed4fdb1d27055aa3faa2c2432283", + "filename": "length-2", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/gridfs/download.json b/tests/UnifiedSpecTests/gridfs/download.json new file mode 100644 index 000000000..48d324621 --- /dev/null +++ b/tests/UnifiedSpecTests/gridfs/download.json @@ -0,0 +1,558 @@ +{ + "description": "gridfs-download", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "gridfs-tests" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0" + } + }, + { + "collection": { + "id": "bucket0_files_collection", + "database": "database0", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "bucket0_chunks_collection", + "database": "database0", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "filename": "length-0-with-empty-chunk", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "c700ed4fdb1d27055aa3faa2c2432283", + "filename": "length-2", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "dd254cdc958e53abaa67da9f797125f5", + "filename": "length-8", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000005" + }, + "length": 10, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "57d83cd477bfb1ccd975ab33d827a92b", + "filename": "length-10", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000006" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "c700ed4fdb1d27055aa3faa2c2432283", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VWZ3iA==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000005" + }, + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000006" + }, + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VWZ3iA==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000007" + }, + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 2, + "data": { + "$binary": { + "base64": "mao=", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000008" + }, + "files_id": { + "$oid": "000000000000000000000006" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "download when length is zero", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "" + } + } + ] + }, + { + "description": "download when length is zero and there is one empty chunk", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000002" + } + }, + "expectResult": { + "$$matchesHexBytes": "" + } + } + ] + }, + { + "description": "download when there is one chunk", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000003" + } + }, + "expectResult": { + "$$matchesHexBytes": "1122" + } + } + ] + }, + { + "description": "download when there are two chunks", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000004" + } + }, + "expectResult": { + "$$matchesHexBytes": "1122334455667788" + } + } + ] + }, + { + "description": "download when there are three chunks", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectResult": { + "$$matchesHexBytes": "112233445566778899aa" + } + } + ] + }, + { + "description": "download when files entry does not exist", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000000" + } + }, + "expectError": { + "isError": true + } + } + ] + }, + { + "description": "download when an intermediate chunk is missing", + "operations": [ + { + "name": "deleteOne", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": { + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isError": true + } + } + ] + }, + { + "description": "download when final chunk is missing", + "operations": [ + { + "name": "deleteOne", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": { + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 2 + } + }, + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isError": true + } + } + ] + }, + { + "description": "download when an intermediate chunk is the wrong size", + "operations": [ + { + "name": "bulkWrite", + "object": "bucket0_chunks_collection", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 1 + }, + "update": { + "$set": { + "data": { + "$binary": { + "base64": "VWZ3", + "subType": "00" + } + } + } + } + } + }, + { + "updateOne": { + "filter": { + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 2 + }, + "update": { + "$set": { + "data": { + "$binary": { + "base64": "iJmq", + "subType": "00" + } + } + } + } + } + } + ] + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2 + } + }, + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isError": true + } + } + ] + }, + { + "description": "download when final chunk is the wrong size", + "operations": [ + { + "name": "updateOne", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": { + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 2 + }, + "update": { + "$set": { + "data": { + "$binary": { + "base64": "mQ==", + "subType": "00" + } + } + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1 + } + }, + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isError": true + } + } + ] + }, + { + "description": "download legacy file with no name", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000006" + } + }, + "expectResult": { + "$$matchesHexBytes": "1122" + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/gridfs/downloadByName.json b/tests/UnifiedSpecTests/gridfs/downloadByName.json new file mode 100644 index 000000000..cd4466395 --- /dev/null +++ b/tests/UnifiedSpecTests/gridfs/downloadByName.json @@ -0,0 +1,330 @@ +{ + "description": "gridfs-downloadByName", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "gridfs-tests" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0" + } + }, + { + "collection": { + "id": "bucket0_files_collection", + "database": "database0", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "bucket0_chunks_collection", + "database": "database0", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "47ed733b8d10be225eceba344d533586", + "filename": "abc", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-02T00:00:00.000Z" + }, + "md5": "b15835f133ff2e27c7cb28117bfae8f4", + "filename": "abc", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-03T00:00:00.000Z" + }, + "md5": "eccbc87e4b5ce2fe28308fd9f2a7baf3", + "filename": "abc", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-04T00:00:00.000Z" + }, + "md5": "f623e75af30e62bbd73d6df5b50bb7b5", + "filename": "abc", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000005" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-05T00:00:00.000Z" + }, + "md5": "4c614360da93c0a041b22e537de151eb", + "filename": "abc", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000001" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "Ig==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "Mw==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "RA==", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000005" + }, + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 0, + "data": { + "$binary": { + "base64": "VQ==", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "downloadByName defaults to latest revision (-1)", + "operations": [ + { + "name": "downloadByName", + "object": "bucket0", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "55" + } + } + ] + }, + { + "description": "downloadByName when revision is 0", + "operations": [ + { + "name": "downloadByName", + "object": "bucket0", + "arguments": { + "filename": "abc", + "revision": 0 + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ] + }, + { + "description": "downloadByName when revision is 1", + "operations": [ + { + "name": "downloadByName", + "object": "bucket0", + "arguments": { + "filename": "abc", + "revision": 1 + }, + "expectResult": { + "$$matchesHexBytes": "22" + } + } + ] + }, + { + "description": "downloadByName when revision is 2", + "operations": [ + { + "name": "downloadByName", + "object": "bucket0", + "arguments": { + "filename": "abc", + "revision": 2 + }, + "expectResult": { + "$$matchesHexBytes": "33" + } + } + ] + }, + { + "description": "downloadByName when revision is -2", + "operations": [ + { + "name": "downloadByName", + "object": "bucket0", + "arguments": { + "filename": "abc", + "revision": -2 + }, + "expectResult": { + "$$matchesHexBytes": "44" + } + } + ] + }, + { + "description": "downloadByName when revision is -1", + "operations": [ + { + "name": "downloadByName", + "object": "bucket0", + "arguments": { + "filename": "abc", + "revision": -1 + }, + "expectResult": { + "$$matchesHexBytes": "55" + } + } + ] + }, + { + "description": "downloadByName when files entry does not exist", + "operations": [ + { + "name": "downloadByName", + "object": "bucket0", + "arguments": { + "filename": "xyz" + }, + "expectError": { + "isError": true + } + } + ] + }, + { + "description": "downloadByName when revision does not exist", + "operations": [ + { + "name": "downloadByName", + "object": "bucket0", + "arguments": { + "filename": "abc", + "revision": 999 + }, + "expectError": { + "isError": true + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/gridfs/upload-disableMD5.json b/tests/UnifiedSpecTests/gridfs/upload-disableMD5.json new file mode 100644 index 000000000..d5a9d6f4a --- /dev/null +++ b/tests/UnifiedSpecTests/gridfs/upload-disableMD5.json @@ -0,0 +1,172 @@ +{ + "description": "gridfs-upload-disableMD5", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "gridfs-tests" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0" + } + }, + { + "collection": { + "id": "bucket0_files_collection", + "database": "database0", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "bucket0_chunks_collection", + "database": "database0", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "upload when length is 0 sans MD5", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "" + }, + "chunkSizeBytes": 4, + "disableMD5": true + }, + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" + }, + "md5": { + "$$exists": false + }, + "filename": "filename" + } + ] + }, + { + "name": "find", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": {} + }, + "expectResult": [] + } + ] + }, + { + "description": "upload when length is 1 sans MD5", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "11" + }, + "chunkSizeBytes": 4, + "disableMD5": true + }, + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" + }, + "md5": { + "$$exists": false + }, + "filename": "filename" + } + ] + }, + { + "name": "find", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/gridfs/upload.json b/tests/UnifiedSpecTests/gridfs/upload.json new file mode 100644 index 000000000..97e18d2bc --- /dev/null +++ b/tests/UnifiedSpecTests/gridfs/upload.json @@ -0,0 +1,616 @@ +{ + "description": "gridfs-upload", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "gridfs-tests" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0" + } + }, + { + "collection": { + "id": "bucket0_files_collection", + "database": "database0", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "bucket0_chunks_collection", + "database": "database0", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "upload when length is 0", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "" + }, + "chunkSizeBytes": 4 + }, + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" + }, + "md5": { + "$$unsetOrMatches": "d41d8cd98f00b204e9800998ecf8427e" + }, + "filename": "filename" + } + ] + }, + { + "name": "find", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": {} + }, + "expectResult": [] + } + ] + }, + { + "description": "upload when length is 1", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "11" + }, + "chunkSizeBytes": 4 + }, + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" + }, + "md5": { + "$$unsetOrMatches": "47ed733b8d10be225eceba344d533586" + }, + "filename": "filename" + } + ] + }, + { + "name": "find", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "upload when length is 3", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "112233" + }, + "chunkSizeBytes": 4 + }, + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "length": 3, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" + }, + "md5": { + "$$unsetOrMatches": "bafae3a174ab91fc70db7a6aa50f4f52" + }, + "filename": "filename" + } + ] + }, + { + "name": "find", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIz", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "upload when length is 4", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "11223344" + }, + "chunkSizeBytes": 4 + }, + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "length": 4, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" + }, + "md5": { + "$$unsetOrMatches": "7e7c77cff5705d1f7574a25ef6662117" + }, + "filename": "filename" + } + ] + }, + { + "name": "find", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "upload when length is 5", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + }, + "chunkSizeBytes": 4 + }, + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "length": 5, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" + }, + "md5": { + "$$unsetOrMatches": "283d4fea5dded59cf837d3047328f5af" + }, + "filename": "filename" + } + ] + }, + { + "name": "find", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": {}, + "sort": { + "n": 1 + } + }, + "expectResult": [ + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } + }, + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VQ==", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "upload when length is 8", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455667788" + }, + "chunkSizeBytes": 4 + }, + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" + }, + "md5": { + "$$unsetOrMatches": "dd254cdc958e53abaa67da9f797125f5" + }, + "filename": "filename" + } + ] + }, + { + "name": "find", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": {}, + "sort": { + "n": 1 + } + }, + "expectResult": [ + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } + }, + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VWZ3iA==", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "upload when contentType is provided", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "11" + }, + "chunkSizeBytes": 4, + "contentType": "image/jpeg" + }, + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" + }, + "md5": { + "$$unsetOrMatches": "47ed733b8d10be225eceba344d533586" + }, + "filename": "filename", + "contentType": "image/jpeg" + } + ] + }, + { + "name": "find", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "upload when metadata is provided", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "11" + }, + "chunkSizeBytes": 4, + "metadata": { + "x": 1 + } + }, + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" + }, + "md5": { + "$$unsetOrMatches": "47ed733b8d10be225eceba344d533586" + }, + "filename": "filename", + "metadata": { + "x": 1 + } + } + ] + }, + { + "name": "find", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/load-balancers/cursors.json b/tests/UnifiedSpecTests/load-balancers/cursors.json new file mode 100644 index 000000000..b11bf2c6f --- /dev/null +++ b/tests/UnifiedSpecTests/load-balancers/cursors.json @@ -0,0 +1,1271 @@ +{ + "description": "cursors are correctly pinned to connections for load-balanced clusters", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + }, + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "coll1" + } + }, + { + "collection": { + "id": "collection2", + "database": "database0", + "collectionName": "coll2" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + }, + { + "collectionName": "coll1", + "databaseName": "database0Name", + "documents": [] + }, + { + "collectionName": "coll2", + "databaseName": "database0Name", + "documents": [] + } + ], + "tests": [ + { + "description": "no connection is pinned if all documents are returned in the initial batch", + "operations": [ + { + "name": "createFindCursor", + "object": "collection0", + "arguments": { + "filter": {} + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {} + }, + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": 0, + "firstBatch": { + "$$type": "array" + }, + "ns": { + "$$type": "string" + } + } + }, + "commandName": "find" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connections are returned when the cursor is drained", + "operations": [ + { + "name": "createFindCursor", + "object": "collection0", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 1 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 2 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 3 + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + }, + { + "name": "close", + "object": "cursor0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {}, + "batchSize": 2 + }, + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": { + "$$type": [ + "int", + "long" + ] + }, + "firstBatch": { + "$$type": "array" + }, + "ns": { + "$$type": "string" + } + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0" + }, + "commandName": "getMore" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": 0, + "ns": { + "$$type": "string" + }, + "nextBatch": { + "$$type": "array" + } + } + }, + "commandName": "getMore" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connections are returned to the pool when the cursor is closed", + "operations": [ + { + "name": "createFindCursor", + "object": "collection0", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "close", + "object": "cursor0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {}, + "batchSize": 2 + }, + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": { + "$$type": [ + "int", + "long" + ] + }, + "firstBatch": { + "$$type": "array" + }, + "ns": { + "$$type": "string" + } + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "killCursors" + } + }, + { + "commandSucceededEvent": { + "commandName": "killCursors" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connections are not returned after an network error during getMore", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "closeConnection": true + } + } + } + }, + { + "name": "createFindCursor", + "object": "collection0", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 1 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 2 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectError": { + "isClientError": true + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "close", + "object": "cursor0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {}, + "batchSize": 2 + }, + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": { + "$$type": [ + "int", + "long" + ] + }, + "firstBatch": { + "$$type": "array" + }, + "ns": { + "$$type": "string" + } + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0" + }, + "commandName": "getMore" + } + }, + { + "commandFailedEvent": { + "commandName": "getMore" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + } + ] + } + ] + }, + { + "description": "pinned connections are returned after a network error during a killCursors request", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "killCursors" + ], + "closeConnection": true + } + } + } + }, + { + "name": "createFindCursor", + "object": "collection0", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "close", + "object": "cursor0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {}, + "batchSize": 2 + }, + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": { + "$$type": [ + "int", + "long" + ] + }, + "firstBatch": { + "$$type": "array" + }, + "ns": { + "$$type": "string" + } + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "killCursors" + } + }, + { + "commandFailedEvent": { + "commandName": "killCursors" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + } + ] + } + ] + }, + { + "description": "pinned connections are not returned to the pool after a non-network error on getMore", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorCode": 7 + } + } + } + }, + { + "name": "createFindCursor", + "object": "collection0", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 1 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 2 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectError": { + "errorCode": 7 + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "close", + "object": "cursor0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {}, + "batchSize": 2 + }, + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": { + "$$type": [ + "int", + "long" + ] + }, + "firstBatch": { + "$$type": "array" + }, + "ns": { + "$$type": "string" + } + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0" + }, + "commandName": "getMore" + } + }, + { + "commandFailedEvent": { + "commandName": "getMore" + } + }, + { + "commandStartedEvent": { + "commandName": "killCursors" + } + }, + { + "commandSucceededEvent": { + "commandName": "killCursors" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "aggregate pins the cursor to a connection", + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [], + "batchSize": 2 + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "cursor": { + "batchSize": 2 + } + }, + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0" + }, + "commandName": "getMore" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": 0, + "ns": { + "$$type": "string" + }, + "nextBatch": { + "$$type": "array" + } + } + }, + "commandName": "getMore" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "listCollections pins the cursor to a connection", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "listCollections", + "object": "database0", + "arguments": { + "filter": {}, + "batchSize": 2 + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "cursor": { + "batchSize": 2 + } + }, + "commandName": "listCollections", + "databaseName": "database0Name" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": { + "$$type": "string" + } + }, + "commandName": "getMore" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": 0, + "ns": { + "$$type": "string" + }, + "nextBatch": { + "$$type": "array" + } + } + }, + "commandName": "getMore" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "listIndexes pins the cursor to a connection", + "operations": [ + { + "name": "createIndex", + "object": "collection0", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "createIndex", + "object": "collection0", + "arguments": { + "keys": { + "y": 1 + }, + "name": "y_1" + } + }, + { + "name": "listIndexes", + "object": "collection0", + "arguments": { + "batchSize": 2 + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createIndexes": "coll0", + "indexes": [ + { + "name": "x_1", + "key": { + "x": 1 + } + } + ] + }, + "commandName": "createIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "createIndexes" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "coll0", + "indexes": [ + { + "name": "y_1", + "key": { + "y": 1 + } + } + ] + }, + "commandName": "createIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "createIndexes" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll0", + "cursor": { + "batchSize": 2 + } + }, + "commandName": "listIndexes", + "databaseName": "database0Name" + } + }, + { + "commandSucceededEvent": { + "commandName": "listIndexes" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0" + }, + "commandName": "getMore" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": 0, + "ns": { + "$$type": "string" + }, + "nextBatch": { + "$$type": "array" + } + } + }, + "commandName": "getMore" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "change streams pin to a connection", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "close", + "object": "changeStream0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "killCursors" + } + }, + { + "commandSucceededEvent": { + "commandName": "killCursors" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/load-balancers/event-monitoring.json b/tests/UnifiedSpecTests/load-balancers/event-monitoring.json new file mode 100644 index 000000000..938c70bf3 --- /dev/null +++ b/tests/UnifiedSpecTests/load-balancers/event-monitoring.json @@ -0,0 +1,184 @@ +{ + "description": "monitoring events include correct fields", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolClearedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "databaseName": "database0", + "collectionName": "coll0", + "documents": [] + } + ], + "tests": [ + { + "description": "command started and succeeded events include serviceId", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "hasServiceId": true + } + }, + { + "commandSucceededEvent": { + "commandName": "insert", + "hasServiceId": true + } + } + ] + } + ] + }, + { + "description": "command failed events include serviceId", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "hasServiceId": true + } + }, + { + "commandFailedEvent": { + "commandName": "find", + "hasServiceId": true + } + } + ] + } + ] + }, + { + "description": "poolClearedEvent events include serviceId", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "hasServiceId": true + } + }, + { + "commandFailedEvent": { + "commandName": "find", + "hasServiceId": true + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "poolClearedEvent": { + "hasServiceId": true + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/load-balancers/lb-connection-establishment.json b/tests/UnifiedSpecTests/load-balancers/lb-connection-establishment.json new file mode 100644 index 000000000..0eaadf30c --- /dev/null +++ b/tests/UnifiedSpecTests/load-balancers/lb-connection-establishment.json @@ -0,0 +1,58 @@ +{ + "description": "connection establishment for load-balanced clusters", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "uriOptions": { + "loadBalanced": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + } + ], + "tests": [ + { + "description": "operations against load balancers fail if URI contains loadBalanced=false", + "skipReason": "servers have not implemented LB support yet so they will not fail the connection handshake in this case", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/load-balancers/non-lb-connection-establishment.json b/tests/UnifiedSpecTests/load-balancers/non-lb-connection-establishment.json new file mode 100644 index 000000000..6aaa7bdf9 --- /dev/null +++ b/tests/UnifiedSpecTests/load-balancers/non-lb-connection-establishment.json @@ -0,0 +1,92 @@ +{ + "description": "connection establishment if loadBalanced is specified for non-load balanced clusters", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "topologies": [ + "single", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "lbTrueClient", + "useMultipleMongoses": false, + "uriOptions": { + "loadBalanced": true + } + } + }, + { + "database": { + "id": "lbTrueDatabase", + "client": "lbTrueClient", + "databaseName": "lbTrueDb" + } + }, + { + "client": { + "id": "lbFalseClient", + "uriOptions": { + "loadBalanced": false + } + } + }, + { + "database": { + "id": "lbFalseDatabase", + "client": "lbFalseClient", + "databaseName": "lbFalseDb" + } + } + ], + "_yamlAnchors": { + "runCommandArguments": [ + { + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + } + } + ] + }, + "tests": [ + { + "description": "operations against non-load balanced clusters fail if URI contains loadBalanced=true", + "operations": [ + { + "name": "runCommand", + "object": "lbTrueDatabase", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "errorContains": "Driver attempted to initialize in load balancing mode, but the server does not support this mode" + } + } + ] + }, + { + "description": "operations against non-load balanced clusters succeed if URI contains loadBalanced=false", + "operations": [ + { + "name": "runCommand", + "object": "lbFalseDatabase", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/load-balancers/sdam-error-handling.json b/tests/UnifiedSpecTests/load-balancers/sdam-error-handling.json new file mode 100644 index 000000000..083c98e94 --- /dev/null +++ b/tests/UnifiedSpecTests/load-balancers/sdam-error-handling.json @@ -0,0 +1,514 @@ +{ + "description": "state change errors are correctly handled", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "_yamlAnchors": { + "observedEvents": [ + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionCheckedOutEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedInEvent", + "connectionClosedEvent", + "poolClearedEvent" + ] + }, + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "singleClient", + "useMultipleMongoses": false, + "uriOptions": { + "appname": "lbSDAMErrorTestClient", + "retryWrites": false + }, + "observeEvents": [ + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionCheckedOutEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedInEvent", + "connectionClosedEvent", + "poolClearedEvent" + ] + } + }, + { + "database": { + "id": "singleDB", + "client": "singleClient", + "databaseName": "singleDB" + } + }, + { + "collection": { + "id": "singleColl", + "database": "singleDB", + "collectionName": "singleColl" + } + }, + { + "client": { + "id": "multiClient", + "useMultipleMongoses": true, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionCheckedOutEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedInEvent", + "connectionClosedEvent", + "poolClearedEvent" + ] + } + }, + { + "database": { + "id": "multiDB", + "client": "multiClient", + "databaseName": "multiDB" + } + }, + { + "collection": { + "id": "multiColl", + "database": "multiDB", + "collectionName": "multiColl" + } + } + ], + "initialData": [ + { + "collectionName": "singleColl", + "databaseName": "singleDB", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + }, + { + "collectionName": "multiColl", + "databaseName": "multiDB", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ], + "tests": [ + { + "description": "only connections for a specific serviceId are closed when pools are cleared", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "createFindCursor", + "object": "multiColl", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "createFindCursor", + "object": "multiColl", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor1" + }, + { + "name": "close", + "object": "cursor0" + }, + { + "name": "close", + "object": "cursor1" + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "multiClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11600 + } + } + } + }, + { + "name": "insertOne", + "object": "multiColl", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "errorCode": 11600 + } + }, + { + "name": "insertOne", + "object": "multiColl", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "multiClient", + "eventType": "cmap", + "events": [ + { + "connectionCreatedEvent": {} + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCreatedEvent": {} + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "poolClearedEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "stale" + } + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "errors during the initial connection hello are ignored", + "runOnRequirements": [ + { + "minServerVersion": "4.9" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": true, + "appName": "lbSDAMErrorTestClient" + } + } + } + }, + { + "name": "insertOne", + "object": "singleColl", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "singleClient", + "eventType": "cmap", + "events": [ + { + "connectionCreatedEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + }, + { + "connectionCheckOutFailedEvent": { + "reason": "connectionError" + } + } + ] + } + ] + }, + { + "description": "errors during authentication are processed", + "runOnRequirements": [ + { + "auth": true + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "closeConnection": true, + "appName": "lbSDAMErrorTestClient" + } + } + } + }, + { + "name": "insertOne", + "object": "singleColl", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "singleClient", + "eventType": "cmap", + "events": [ + { + "connectionCreatedEvent": {} + }, + { + "poolClearedEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + }, + { + "connectionCheckOutFailedEvent": { + "reason": "connectionError" + } + } + ] + } + ] + }, + { + "description": "stale errors are ignored", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "getMore" + ], + "closeConnection": true + } + } + } + }, + { + "name": "createFindCursor", + "object": "singleColl", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "createFindCursor", + "object": "singleColl", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor1" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectError": { + "isClientError": true + } + }, + { + "name": "close", + "object": "cursor0" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor1" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor1" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor1", + "expectError": { + "isClientError": true + } + }, + { + "name": "close", + "object": "cursor1" + } + ], + "expectEvents": [ + { + "client": "singleClient", + "eventType": "cmap", + "events": [ + { + "connectionCreatedEvent": {} + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCreatedEvent": {} + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "poolClearedEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/load-balancers/server-selection.json b/tests/UnifiedSpecTests/load-balancers/server-selection.json new file mode 100644 index 000000000..00c7e4c95 --- /dev/null +++ b/tests/UnifiedSpecTests/load-balancers/server-selection.json @@ -0,0 +1,82 @@ +{ + "description": "server selection for load-balanced clusters", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "readPreference": { + "mode": "secondaryPreferred" + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [] + } + ], + "tests": [ + { + "description": "$readPreference is sent for load-balanced clusters", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {}, + "$readPreference": { + "mode": "secondaryPreferred" + } + }, + "commandName": "find", + "databaseName": "database0Name" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/load-balancers/transactions.json b/tests/UnifiedSpecTests/load-balancers/transactions.json new file mode 100644 index 000000000..0dd04ee85 --- /dev/null +++ b/tests/UnifiedSpecTests/load-balancers/transactions.json @@ -0,0 +1,1621 @@ +{ + "description": "transactions are correctly pinned to connections for load-balanced clusters", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ], + "_yamlAnchors": { + "documents": [ + { + "_id": 4 + } + ] + }, + "tests": [ + { + "description": "sessions are reused in LB mode", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "assertSameLsidOnLastTwoCommands", + "object": "testRunner", + "arguments": { + "client": "client0" + } + } + ] + }, + { + "description": "all operations go to the same mongos", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "commitTransaction", + "object": "session0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + } + ] + } + ] + }, + { + "description": "transaction can be committed multiple times", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is not released after a non-transient CRUD error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 51 + } + } + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + }, + "expectError": { + "errorCode": 51, + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is not released after a non-transient commit error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 51 + } + } + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "commitTransaction", + "object": "session0", + "expectError": { + "errorCode": 51, + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is released after a non-transient abort error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 51 + } + } + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is released after a transient non-network CRUD error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 24 + } + } + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + }, + "expectError": { + "errorCode": 24, + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + }, + { + "name": "abortTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is released after a transient network CRUD error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + }, + "expectError": { + "isClientError": true, + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + }, + { + "name": "abortTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is released after a transient non-network commit error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 24 + } + } + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "commitTransaction", + "object": "session0", + "expectError": { + "errorCode": 24, + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is released after a transient network commit error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "closeConnection": true + } + } + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "commitTransaction", + "object": "session0", + "ignoreResultAndError": true + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is released after a transient non-network abort error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 24 + } + } + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is released after a transient network abort error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "closeConnection": true + } + } + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is released on successful abort", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is returned when a new transaction is started", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "commitTransaction", + "object": "session0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + } + ] + } + ] + }, + { + "description": "pinned connection is returned when a non-transaction operation uses the session", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "a connection can be shared by a transaction and a cursor", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "createFindCursor", + "object": "collection0", + "arguments": { + "filter": {}, + "batchSize": 2, + "session": "session0" + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "close", + "object": "cursor0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + }, + { + "name": "abortTransaction", + "object": "session0" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "killCursors" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/load-balancers/wait-queue-timeouts.json b/tests/UnifiedSpecTests/load-balancers/wait-queue-timeouts.json new file mode 100644 index 000000000..3dc6e46cf --- /dev/null +++ b/tests/UnifiedSpecTests/load-balancers/wait-queue-timeouts.json @@ -0,0 +1,153 @@ +{ + "description": "wait queue timeout errors include details about checked out connections", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "uriOptions": { + "maxPoolSize": 1, + "waitQueueTimeoutMS": 50 + }, + "observeEvents": [ + "connectionCheckedOutEvent", + "connectionCheckOutFailedEvent" + ] + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ], + "tests": [ + { + "description": "wait queue timeout errors include cursor statistics", + "operations": [ + { + "name": "createFindCursor", + "object": "collection0", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "isClientError": true, + "errorContains": "maxPoolSize: 1, connections in use by cursors: 1, connections in use by transactions: 0, connections in use by other operations: 0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckOutFailedEvent": {} + } + ] + } + ] + }, + { + "description": "wait queue timeout errors include transaction statistics", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "isClientError": true, + "errorContains": "maxPoolSize: 1, connections in use by cursors: 0, connections in use by transactions: 1, connections in use by other operations: 0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckOutFailedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/retryable-writes/bulkWrite-serverErrors.json b/tests/UnifiedSpecTests/retryable-writes/bulkWrite-serverErrors.json new file mode 100644 index 000000000..aed210ec2 --- /dev/null +++ b/tests/UnifiedSpecTests/retryable-writes/bulkWrite-serverErrors.json @@ -0,0 +1,205 @@ +{ + "description": "retryable-writes bulkWrite serverErrors", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite succeeds after retryable writeConcernError in first batch", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 2 + } + } + } + ] + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": { + "0": 3 + } + }, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 3, + "x": 33 + } + ] + }, + "commandName": "insert", + "databaseName": "retryable-writes-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 3, + "x": 33 + } + ] + }, + "commandName": "insert", + "databaseName": "retryable-writes-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "coll", + "deletes": [ + { + "q": { + "_id": 2 + }, + "limit": 1 + } + ] + }, + "commandName": "delete", + "databaseName": "retryable-writes-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/retryable-writes/handshakeError.json b/tests/UnifiedSpecTests/retryable-writes/handshakeError.json new file mode 100644 index 000000000..df37bd723 --- /dev/null +++ b/tests/UnifiedSpecTests/retryable-writes/handshakeError.json @@ -0,0 +1,1797 @@ +{ + "description": "retryable writes handshake failures", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "auth": true + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "connectionCheckOutStartedEvent", + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-handshake-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "collection.insertOne succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.insertOne succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.insertMany succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.insertMany succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.deleteOne succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandSucceededEvent": { + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "collection.deleteOne succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandSucceededEvent": { + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "collection.replaceOne succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.replaceOne succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.updateOne succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.updateOne succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndDelete succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndDelete succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndReplace succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndReplace succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndUpdate succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndUpdate succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.bulkWrite succeeds after retryable handshake network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.bulkWrite succeeds after retryable handshake server error (ShutdownInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping", + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-writes-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/retryable-writes/insertOne-noWritesPerformedError.json b/tests/UnifiedSpecTests/retryable-writes/insertOne-noWritesPerformedError.json new file mode 100644 index 000000000..3194e91c5 --- /dev/null +++ b/tests/UnifiedSpecTests/retryable-writes/insertOne-noWritesPerformedError.json @@ -0,0 +1,90 @@ +{ + "description": "retryable-writes insertOne noWritesPerformedErrors", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "6.0", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "no-writes-performed-collection" + } + } + ], + "tests": [ + { + "description": "InsertOne fails after NoWritesPerformed error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 64, + "errorLabels": [ + "NoWritesPerformed", + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "errorCode": 64, + "errorLabelsContain": [ + "NoWritesPerformed", + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "no-writes-performed-collection", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/retryable-writes/insertOne-serverErrors.json b/tests/UnifiedSpecTests/retryable-writes/insertOne-serverErrors.json new file mode 100644 index 000000000..a87f45169 --- /dev/null +++ b/tests/UnifiedSpecTests/retryable-writes/insertOne-serverErrors.json @@ -0,0 +1,173 @@ +{ + "description": "retryable-writes insertOne serverErrors", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "InsertOne succeeds after retryable writeConcernError", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 3, + "x": 33 + } + ] + }, + "commandName": "insert", + "databaseName": "retryable-writes-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 3, + "x": 33 + } + ] + }, + "commandName": "insert", + "databaseName": "retryable-writes-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/run-command/runCommand.json b/tests/UnifiedSpecTests/run-command/runCommand.json new file mode 100644 index 000000000..9c7f912f9 --- /dev/null +++ b/tests/UnifiedSpecTests/run-command/runCommand.json @@ -0,0 +1,573 @@ +{ + "description": "runCommand", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "collection", + "database": "db", + "collectionName": "collection" + } + }, + { + "database": { + "id": "dbWithRC", + "client": "client", + "databaseName": "dbWithRC", + "databaseOptions": { + "readConcern": { + "level": "local" + } + } + } + }, + { + "database": { + "id": "dbWithWC", + "client": "client", + "databaseName": "dbWithWC", + "databaseOptions": { + "writeConcern": { + "w": 0 + } + } + } + }, + { + "session": { + "id": "session", + "client": "client" + } + }, + { + "client": { + "id": "clientWithStableApi", + "observeEvents": [ + "commandStartedEvent" + ], + "serverApi": { + "version": "1", + "strict": true + } + } + }, + { + "database": { + "id": "dbWithStableApi", + "client": "clientWithStableApi", + "databaseName": "dbWithStableApi" + } + } + ], + "initialData": [ + { + "collectionName": "collection", + "databaseName": "db", + "documents": [] + } + ], + "tests": [ + { + "description": "always attaches $db and implicit lsid to given command and omits default readPreference", + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1, + "$db": "db", + "lsid": { + "$$exists": true + }, + "$readPreference": { + "$$exists": false + } + }, + "commandName": "ping" + } + } + ] + } + ] + }, + { + "description": "attaches the provided session lsid to given command", + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + }, + "session": "session" + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1, + "lsid": { + "$$sessionLsid": "session" + }, + "$db": "db" + }, + "commandName": "ping" + } + } + ] + } + ] + }, + { + "description": "attaches the provided $readPreference to given command", + "runOnRequirements": [ + { + "topologies": [ + "replicaset", + "sharded-replicaset", + "load-balanced", + "sharded" + ] + } + ], + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + }, + "readPreference": { + "mode": "nearest" + } + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1, + "$readPreference": { + "mode": "nearest" + }, + "$db": "db" + }, + "commandName": "ping" + } + } + ] + } + ] + }, + { + "description": "does not attach $readPreference to given command on standalone", + "runOnRequirements": [ + { + "topologies": [ + "single" + ] + } + ], + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + }, + "readPreference": { + "mode": "nearest" + } + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1, + "$readPreference": { + "$$exists": false + }, + "$db": "db" + }, + "commandName": "ping" + } + } + ] + } + ] + }, + { + "description": "does not attach primary $readPreference to given command", + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + }, + "readPreference": { + "mode": "primary" + } + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1, + "$readPreference": { + "$$exists": false + }, + "$db": "db" + }, + "commandName": "ping" + } + } + ] + } + ] + }, + { + "description": "does not inherit readConcern specified at the db level", + "operations": [ + { + "name": "runCommand", + "object": "dbWithRC", + "arguments": { + "commandName": "aggregate", + "command": { + "aggregate": "collection", + "pipeline": [], + "cursor": {} + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection", + "readConcern": { + "$$exists": false + }, + "$db": "dbWithRC" + }, + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "does not inherit writeConcern specified at the db level", + "operations": [ + { + "name": "runCommand", + "object": "dbWithWC", + "arguments": { + "commandName": "insert", + "command": { + "insert": "collection", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true + } + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "collection", + "writeConcern": { + "$$exists": false + }, + "$db": "dbWithWC" + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "does not retry retryable errors on given command", + "runOnRequirements": [ + { + "minServerVersion": "4.2" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "ping" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "attaches transaction fields to given command", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.2", + "topologies": [ + "sharded-replicaset", + "load-balanced" + ] + } + ], + "operations": [ + { + "name": "withTransaction", + "object": "session", + "arguments": { + "callback": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "session": "session", + "commandName": "insert", + "command": { + "insert": "collection", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "collection", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session" + }, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "db" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session" + }, + "txnNumber": 1, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "readConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "attaches apiVersion fields to given command when stableApi is configured on the client", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "dbWithStableApi", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "clientWithStableApi", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1, + "$db": "dbWithStableApi", + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + }, + "commandName": "ping" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/sessions/driver-sessions-dirty-session-errors.json b/tests/UnifiedSpecTests/sessions/driver-sessions-dirty-session-errors.json new file mode 100644 index 000000000..6aa1da1df --- /dev/null +++ b/tests/UnifiedSpecTests/sessions/driver-sessions-dirty-session-errors.json @@ -0,0 +1,968 @@ +{ + "description": "driver-sessions-dirty-session-errors", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "session-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "session-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ], + "tests": [ + { + "description": "Dirty explicit session is discarded (insert)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "name": "assertSessionNotDirty", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "name": "assertSessionDirty", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "assertSessionDirty", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "endSession", + "object": "session0" + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": -1 + } + }, + "expectResult": [] + }, + { + "name": "assertDifferentLsidOnLastTwoCommands", + "object": "testRunner", + "arguments": { + "client": "client0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1 + }, + "commandName": "insert", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1 + }, + "commandName": "insert", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 3 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 2 + }, + "commandName": "insert", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": -1 + }, + "lsid": { + "$$type": "object" + } + }, + "commandName": "find", + "databaseName": "session-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "session-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ] + }, + { + "description": "Dirty explicit session is discarded (findAndModify)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "closeConnection": true + } + } + } + }, + { + "name": "assertSessionNotDirty", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1 + } + }, + { + "name": "assertSessionDirty", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "endSession", + "object": "session0" + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": -1 + } + }, + "expectResult": [] + }, + { + "name": "assertDifferentLsidOnLastTwoCommands", + "object": "testRunner", + "arguments": { + "client": "client0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "new": false, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "new": false, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": -1 + }, + "lsid": { + "$$type": "object" + } + }, + "commandName": "find", + "databaseName": "session-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "session-tests", + "documents": [ + { + "_id": 1, + "x": 1 + } + ] + } + ] + }, + { + "description": "Dirty implicit session is discarded (insert)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": -1 + } + }, + "expectResult": [] + }, + { + "name": "assertDifferentLsidOnLastTwoCommands", + "object": "testRunner", + "arguments": { + "client": "client0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$type": "object" + }, + "txnNumber": 1 + }, + "commandName": "insert", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$type": "object" + }, + "txnNumber": 1 + }, + "commandName": "insert", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": -1 + }, + "lsid": { + "$$type": "object" + } + }, + "commandName": "find", + "databaseName": "session-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "session-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "Dirty implicit session is discarded (findAndModify)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "closeConnection": true + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1 + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": -1 + } + }, + "expectResult": [] + }, + { + "name": "assertDifferentLsidOnLastTwoCommands", + "object": "testRunner", + "arguments": { + "client": "client0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "new": false, + "lsid": { + "$$type": "object" + }, + "txnNumber": 1, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "new": false, + "lsid": { + "$$type": "object" + }, + "txnNumber": 1, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": -1 + }, + "lsid": { + "$$type": "object" + } + }, + "commandName": "find", + "databaseName": "session-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "session-tests", + "documents": [ + { + "_id": 1, + "x": 1 + } + ] + } + ] + }, + { + "description": "Dirty implicit session is discarded (read returning cursor)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 1 + } + ] + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": -1 + } + }, + "expectResult": [] + }, + { + "name": "assertDifferentLsidOnLastTwoCommands", + "object": "testRunner", + "arguments": { + "client": "client0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "lsid": { + "$$type": "object" + } + }, + "commandName": "aggregate", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "lsid": { + "$$type": "object" + } + }, + "commandName": "aggregate", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": -1 + }, + "lsid": { + "$$type": "object" + } + }, + "commandName": "find", + "databaseName": "session-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "session-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Dirty implicit session is discarded (read not returning cursor)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "filter": {} + }, + "expectResult": 1 + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": -1 + } + }, + "expectResult": [] + }, + { + "name": "assertDifferentLsidOnLastTwoCommands", + "object": "testRunner", + "arguments": { + "client": "client0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "lsid": { + "$$type": "object" + } + }, + "commandName": "aggregate", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "lsid": { + "$$type": "object" + } + }, + "commandName": "aggregate", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": -1 + }, + "lsid": { + "$$type": "object" + } + }, + "commandName": "find", + "databaseName": "session-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "session-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/sessions/driver-sessions-server-support.json b/tests/UnifiedSpecTests/sessions/driver-sessions-server-support.json new file mode 100644 index 000000000..55312b32e --- /dev/null +++ b/tests/UnifiedSpecTests/sessions/driver-sessions-server-support.json @@ -0,0 +1,256 @@ +{ + "description": "driver-sessions-server-support", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "session-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "session-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ], + "tests": [ + { + "description": "Server supports explicit sessions", + "operations": [ + { + "name": "assertSessionNotDirty", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "name": "assertSessionNotDirty", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "endSession", + "object": "session0" + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": -1 + } + }, + "expectResult": [] + }, + { + "name": "assertSameLsidOnLastTwoCommands", + "object": "testRunner", + "arguments": { + "client": "client0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + } + }, + "commandName": "insert", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": -1 + }, + "lsid": { + "$$sessionLsid": "session0" + } + }, + "commandName": "find", + "databaseName": "session-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "session-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "Server supports implicit sessions", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": -1 + } + }, + "expectResult": [] + }, + { + "name": "assertSameLsidOnLastTwoCommands", + "object": "testRunner", + "arguments": { + "client": "client0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$type": "object" + } + }, + "commandName": "insert", + "databaseName": "session-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "_id": -1 + }, + "lsid": { + "$$type": "object" + } + }, + "commandName": "find", + "databaseName": "session-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "session-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/sessions/implicit-sessions-default-causal-consistency.json b/tests/UnifiedSpecTests/sessions/implicit-sessions-default-causal-consistency.json new file mode 100644 index 000000000..517c8ebc6 --- /dev/null +++ b/tests/UnifiedSpecTests/sessions/implicit-sessions-default-causal-consistency.json @@ -0,0 +1,318 @@ +{ + "description": "implicit sessions default causal consistency", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "implicit-cc-tests" + } + }, + { + "collection": { + "id": "collectionDefault", + "database": "database0", + "collectionName": "coll-default" + } + }, + { + "collection": { + "id": "collectionSnapshot", + "database": "database0", + "collectionName": "coll-snapshot", + "collectionOptions": { + "readConcern": { + "level": "snapshot" + } + } + } + }, + { + "collection": { + "id": "collectionlinearizable", + "database": "database0", + "collectionName": "coll-linearizable", + "collectionOptions": { + "readConcern": { + "level": "linearizable" + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll-default", + "databaseName": "implicit-cc-tests", + "documents": [ + { + "_id": 1, + "x": "default" + } + ] + }, + { + "collectionName": "coll-snapshot", + "databaseName": "implicit-cc-tests", + "documents": [ + { + "_id": 1, + "x": "snapshot" + } + ] + }, + { + "collectionName": "coll-linearizable", + "databaseName": "implicit-cc-tests", + "documents": [ + { + "_id": 1, + "x": "linearizable" + } + ] + } + ], + "tests": [ + { + "description": "readConcern is not sent on retried read in implicit session when readConcern level is not specified", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + } + } + }, + { + "name": "find", + "object": "collectionDefault", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": "default" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll-default", + "filter": {}, + "readConcern": { + "$$exists": false + } + }, + "databaseName": "implicit-cc-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll-default", + "filter": {}, + "readConcern": { + "$$exists": false + } + }, + "databaseName": "implicit-cc-tests" + } + } + ] + } + ] + }, + { + "description": "afterClusterTime is not sent on retried read in implicit session when readConcern level is snapshot", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + } + } + }, + { + "name": "find", + "object": "collectionSnapshot", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": "snapshot" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll-snapshot", + "filter": {}, + "readConcern": { + "level": "snapshot", + "afterClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "implicit-cc-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll-snapshot", + "filter": {}, + "readConcern": { + "level": "snapshot", + "afterClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "implicit-cc-tests" + } + } + ] + } + ] + }, + { + "description": "afterClusterTime is not sent on retried read in implicit session when readConcern level is linearizable", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + } + } + }, + { + "name": "find", + "object": "collectionlinearizable", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": "linearizable" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll-linearizable", + "filter": {}, + "readConcern": { + "level": "linearizable", + "afterClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "implicit-cc-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll-linearizable", + "filter": {}, + "readConcern": { + "level": "linearizable", + "afterClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "implicit-cc-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-client-error.json b/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-client-error.json new file mode 100644 index 000000000..208e4cfe6 --- /dev/null +++ b/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-client-error.json @@ -0,0 +1,128 @@ +{ + "description": "snapshot-sessions-not-supported-client-error", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "maxServerVersion": "4.4.99" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + }, + { + "session": { + "id": "session0", + "client": "client0", + "sessionOptions": { + "snapshot": true + } + } + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "Client error on find with snapshot", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": {} + }, + "expectError": { + "isClientError": true, + "errorContains": "Snapshot reads require MongoDB 5.0 or later" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Client error on aggregate with snapshot", + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "session": "session0", + "pipeline": [] + }, + "expectError": { + "isClientError": true, + "errorContains": "Snapshot reads require MongoDB 5.0 or later" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + }, + { + "description": "Client error on distinct with snapshot", + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "session": "session0" + }, + "expectError": { + "isClientError": true, + "errorContains": "Snapshot reads require MongoDB 5.0 or later" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-server-error.json b/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-server-error.json new file mode 100644 index 000000000..79213f314 --- /dev/null +++ b/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-server-error.json @@ -0,0 +1,187 @@ +{ + "description": "snapshot-sessions-not-supported-server-error", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "5.0", + "topologies": [ + "single" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + }, + { + "session": { + "id": "session0", + "client": "client0", + "sessionOptions": { + "snapshot": true + } + } + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "Server returns an error on find with snapshot", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "Server returns an error on aggregate with snapshot", + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "session": "session0", + "pipeline": [] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "Server returns an error on distinct with snapshot", + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "session": "session0" + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "distinct" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/sessions/snapshot-sessions-unsupported-ops.json b/tests/UnifiedSpecTests/sessions/snapshot-sessions-unsupported-ops.json new file mode 100644 index 000000000..c41f74d33 --- /dev/null +++ b/tests/UnifiedSpecTests/sessions/snapshot-sessions-unsupported-ops.json @@ -0,0 +1,493 @@ +{ + "description": "snapshot-sessions-unsupported-ops", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "5.0", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + }, + { + "session": { + "id": "session0", + "client": "client0", + "sessionOptions": { + "snapshot": true + } + } + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "Server returns an error on insertOne with snapshot", + "runOnRequirements": [ + { + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 22, + "x": 22 + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Server returns an error on insertMany with snapshot", + "runOnRequirements": [ + { + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "session": "session0", + "documents": [ + { + "_id": 22, + "x": 22 + }, + { + "_id": 33, + "x": 33 + } + ] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Server returns an error on deleteOne with snapshot", + "runOnRequirements": [ + { + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "Server returns an error on updateOne with snapshot", + "runOnRequirements": [ + { + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "Server returns an error on findOneAndUpdate with snapshot", + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "Server returns an error on listDatabases with snapshot", + "operations": [ + { + "name": "listDatabases", + "object": "client0", + "arguments": { + "session": "session0" + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1, + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "Server returns an error on listCollections with snapshot", + "operations": [ + { + "name": "listCollections", + "object": "database0", + "arguments": { + "session": "session0" + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "Server returns an error on listIndexes with snapshot", + "operations": [ + { + "name": "listIndexes", + "object": "collection0", + "arguments": { + "session": "session0" + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "Server returns an error on runCommand with snapshot", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "session": "session0", + "commandName": "listCollections", + "command": { + "listCollections": 1 + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/sessions/snapshot-sessions.json b/tests/UnifiedSpecTests/sessions/snapshot-sessions.json new file mode 100644 index 000000000..260f8b6f4 --- /dev/null +++ b/tests/UnifiedSpecTests/sessions/snapshot-sessions.json @@ -0,0 +1,993 @@ +{ + "description": "snapshot-sessions", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "5.0", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "findAndModify", + "insert", + "update" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0", + "collectionOptions": { + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "session": { + "id": "session0", + "client": "client0", + "sessionOptions": { + "snapshot": true + } + } + }, + { + "session": { + "id": "session1", + "client": "client0", + "sessionOptions": { + "snapshot": true + } + } + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "Find operation with snapshot", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 1, + "x": 12 + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session1", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 12 + } + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 1, + "x": 13 + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 13 + } + ] + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session1", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 12 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "Distinct operation with snapshot", + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 11 + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 2, + "x": 12 + } + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "session": "session1" + }, + "expectResult": [ + 11, + 12 + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 2, + "x": 13 + } + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {} + }, + "expectResult": [ + 11, + 13 + ] + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 11 + ] + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "session": "session1" + }, + "expectResult": [ + 11, + 12 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "Aggregate operation with snapshot", + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ], + "session": "session0" + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 1, + "x": 12 + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ], + "session": "session1" + }, + "expectResult": [ + { + "_id": 1, + "x": 12 + } + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 1, + "x": 13 + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 1, + "x": 13 + } + ] + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ], + "session": "session0" + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ], + "session": "session1" + }, + "expectResult": [ + { + "_id": 1, + "x": 12 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "countDocuments operation with snapshot", + "operations": [ + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "filter": {}, + "session": "session0" + }, + "expectResult": 2 + }, + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "filter": {}, + "session": "session0" + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "Mixed operation with snapshot", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 1, + "x": 12 + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 12 + } + ] + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ], + "session": "session0" + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 11 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "Write commands with snapshot session do not affect snapshot reads", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 22, + "x": 33 + } + } + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "First snapshot read does not send atClusterTime", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "session": "session0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + }, + "commandName": "find", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "StartTransaction fails in snapshot session", + "operations": [ + { + "name": "startTransaction", + "object": "session0", + "expectError": { + "isError": true, + "isClientError": true, + "errorContains": "Transactions are not supported in snapshot sessions" + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/transactions/do-not-retry-read-in-transaction.json b/tests/UnifiedSpecTests/transactions/do-not-retry-read-in-transaction.json new file mode 100644 index 000000000..6d9dc704b --- /dev/null +++ b/tests/UnifiedSpecTests/transactions/do-not-retry-read-in-transaction.json @@ -0,0 +1,115 @@ +{ + "description": "do not retry read in a transaction", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.0.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.2.0", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "uriOptions": { + "retryReads": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-read-in-transaction-test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "tests": [ + { + "description": "find does not retry in a transaction", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "session": "session0" + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "startTransaction": true + }, + "commandName": "find", + "databaseName": "retryable-read-in-transaction-test" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/transactions/mongos-unpin.json b/tests/UnifiedSpecTests/transactions/mongos-unpin.json new file mode 100644 index 000000000..356f4fd9b --- /dev/null +++ b/tests/UnifiedSpecTests/transactions/mongos-unpin.json @@ -0,0 +1,437 @@ +{ + "description": "mongos-unpin", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "mongos-unpin-db" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "mongos-unpin-db", + "documents": [] + } + ], + "_yamlAnchors": { + "anchors": 24 + }, + "tests": [ + { + "description": "unpin after TransientTransactionError error on commit", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 24 + } + } + } + }, + { + "name": "commitTransaction", + "object": "session0", + "expectError": { + "errorCode": 24, + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "UnknownTransactionCommitResult" + ] + } + }, + { + "name": "assertSessionUnpinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" + } + ] + }, + { + "description": "unpin on successful abort", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" + }, + { + "name": "assertSessionUnpinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + } + ] + }, + { + "description": "unpin after non-transient error on abort", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 24 + } + } + } + }, + { + "name": "abortTransaction", + "object": "session0" + }, + { + "name": "assertSessionUnpinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" + } + ] + }, + { + "description": "unpin after TransientTransactionError error on abort", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "abortTransaction", + "object": "session0" + }, + { + "name": "assertSessionUnpinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" + } + ] + }, + { + "description": "unpin when a new transaction is started", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "assertSessionPinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "assertSessionUnpinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + } + ] + }, + { + "description": "unpin when a non-transaction write operation uses a session", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "assertSessionPinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "assertSessionUnpinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + } + ] + }, + { + "description": "unpin when a non-transaction read operation uses a session", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "assertSessionPinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "assertSessionUnpinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/transactions/retryable-abort-handshake.json b/tests/UnifiedSpecTests/transactions/retryable-abort-handshake.json new file mode 100644 index 000000000..4ad56e2f2 --- /dev/null +++ b/tests/UnifiedSpecTests/transactions/retryable-abort-handshake.json @@ -0,0 +1,204 @@ +{ + "description": "retryable abortTransaction on handshake errors", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "serverless": "forbid", + "auth": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "connectionCheckOutStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-handshake-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "session": { + "id": "session1", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-handshake-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "AbortTransaction succeeds after handshake network error", + "skipReason": "DRIVERS-2032: Pinned servers need to be checked if they are still selectable", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "session": "session1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "saslContinue", + "ping" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + }, + "session": "session1" + }, + "expectError": { + "isError": true + } + }, + { + "name": "abortTransaction", + "object": "session0" + } + ], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "startTransaction": true + }, + "commandName": "insert", + "databaseName": "retryable-handshake-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-handshake-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-handshake-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/transactions/retryable-commit-handshake.json b/tests/UnifiedSpecTests/transactions/retryable-commit-handshake.json new file mode 100644 index 000000000..d9315a8fc --- /dev/null +++ b/tests/UnifiedSpecTests/transactions/retryable-commit-handshake.json @@ -0,0 +1,211 @@ +{ + "description": "retryable commitTransaction on handshake errors", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "serverless": "forbid", + "auth": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "connectionCheckOutStartedEvent" + ], + "uriOptions": { + "retryWrites": false + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-handshake-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "session": { + "id": "session1", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-handshake-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "CommitTransaction succeeds after handshake network error", + "skipReason": "DRIVERS-2032: Pinned servers need to be checked if they are still selectable", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "session": "session1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "saslContinue", + "ping" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + }, + "session": "session1" + }, + "expectError": { + "isError": true + } + }, + { + "name": "commitTransaction", + "object": "session0" + } + ], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "startTransaction": true + }, + "commandName": "insert", + "databaseName": "retryable-handshake-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-handshake-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-handshake-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/assertNumberConnectionsCheckedOut.json b/tests/UnifiedSpecTests/valid-fail/assertNumberConnectionsCheckedOut.json new file mode 100644 index 000000000..9799bb2f6 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/assertNumberConnectionsCheckedOut.json @@ -0,0 +1,63 @@ +{ + "description": "assertNumberConnectionsCheckedOut", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true + } + } + ], + "tests": [ + { + "description": "operation fails if client field is not specified", + "operations": [ + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "connections": 1 + } + } + ] + }, + { + "description": "operation fails if connections field is not specified", + "operations": [ + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0" + } + } + ] + }, + { + "description": "operation fails if client entity does not exist", + "operations": [ + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client1" + } + } + ] + }, + { + "description": "operation fails if number of connections is incorrect", + "operations": [ + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-client-apiVersion-unsupported.json b/tests/UnifiedSpecTests/valid-fail/entity-client-apiVersion-unsupported.json new file mode 100644 index 000000000..d92d23dca --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/entity-client-apiVersion-unsupported.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-apiVersion-unsupported", + "schemaVersion": "1.1", + "createEntities": [ + { + "client": { + "id": "client0", + "serverApi": { + "version": "server_will_never_support_this_api_version" + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.json b/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.json new file mode 100644 index 000000000..8c0c4d204 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.json @@ -0,0 +1,28 @@ +{ + "description": "entity-client-storeEventsAsEntities-conflict_with_client_id", + "schemaVersion": "1.2", + "createEntities": [ + { + "client": { + "id": "client0", + "storeEventsAsEntities": [ + { + "id": "client0", + "events": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent" + ] + } + ] + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.json b/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.json new file mode 100644 index 000000000..77bc4abf2 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.json @@ -0,0 +1,43 @@ +{ + "description": "entity-client-storeEventsAsEntities-conflict_within_different_array", + "schemaVersion": "1.2", + "createEntities": [ + { + "client": { + "id": "client0", + "storeEventsAsEntities": [ + { + "id": "events", + "events": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent" + ] + } + ] + } + }, + { + "client": { + "id": "client1", + "storeEventsAsEntities": [ + { + "id": "events", + "events": [ + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + ] + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.json b/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.json new file mode 100644 index 000000000..e1a949988 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.json @@ -0,0 +1,36 @@ +{ + "description": "entity-client-storeEventsAsEntities-conflict_within_same_array", + "schemaVersion": "1.2", + "createEntities": [ + { + "client": { + "id": "client0", + "storeEventsAsEntities": [ + { + "id": "events", + "events": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent" + ] + }, + { + "id": "events", + "events": [ + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + ] + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-findCursor-malformed.json b/tests/UnifiedSpecTests/valid-fail/entity-findCursor-malformed.json new file mode 100644 index 000000000..0956efa4c --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/entity-findCursor-malformed.json @@ -0,0 +1,44 @@ +{ + "description": "entity-findCursor-malformed", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "databaseName": "database0Name", + "collectionName": "coll0", + "documents": [] + } + ], + "tests": [ + { + "description": "createFindCursor fails if filter is not specified", + "operations": [ + { + "name": "createFindCursor", + "object": "collection0", + "saveResultAsEntity": "cursor0" + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-findCursor.json b/tests/UnifiedSpecTests/valid-fail/entity-findCursor.json new file mode 100644 index 000000000..389e448c0 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/entity-findCursor.json @@ -0,0 +1,52 @@ +{ + "description": "entity-findCursor", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "databaseName": "database0Name", + "collectionName": "coll0", + "documents": [] + } + ], + "tests": [ + { + "description": "iterateUntilDocumentOrError fails if it references a nonexistent entity", + "operations": [ + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0" + } + ] + }, + { + "description": "close fails if it references a nonexistent entity", + "operations": [ + { + "name": "close", + "object": "cursor0" + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError-malformed.json b/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError-malformed.json new file mode 100644 index 000000000..b64779c72 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError-malformed.json @@ -0,0 +1,48 @@ +{ + "description": "ignoreResultAndError-malformed", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [] + } + ], + "tests": [ + { + "description": "malformed operation fails if ignoreResultAndError is true", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "foo": "bar" + }, + "ignoreResultAndError": true + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError.json b/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError.json new file mode 100644 index 000000000..01b2421a9 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError.json @@ -0,0 +1,59 @@ +{ + "description": "ignoreResultAndError", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [] + } + ], + "tests": [ + { + "description": "operation errors are not ignored if ignoreResultAndError is false", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + }, + "ignoreResultAndError": false + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_aws_kms_credentials.json b/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_aws_kms_credentials.json new file mode 100644 index 000000000..e62de8003 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_aws_kms_credentials.json @@ -0,0 +1,36 @@ +{ + "description": "kmsProviders-missing_aws_kms_credentials", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": "accessKeyId" + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_azure_kms_credentials.json b/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_azure_kms_credentials.json new file mode 100644 index 000000000..8ef805d0f --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_azure_kms_credentials.json @@ -0,0 +1,36 @@ +{ + "description": "kmsProviders-missing_azure_kms_credentials", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "azure": { + "tenantId": "tenantId" + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json b/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json new file mode 100644 index 000000000..c6da1ce58 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json @@ -0,0 +1,36 @@ +{ + "description": "kmsProviders-missing_gcp_kms_credentials", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "gcp": { + "email": "email" + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/kmsProviders-no_kms.json b/tests/UnifiedSpecTests/valid-fail/kmsProviders-no_kms.json new file mode 100644 index 000000000..57499b4ea --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/kmsProviders-no_kms.json @@ -0,0 +1,32 @@ +{ + "description": "clientEncryptionOpts-no_kms", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": {} + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/operation-failure.json b/tests/UnifiedSpecTests/valid-fail/operation-failure.json new file mode 100644 index 000000000..8f6cae152 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/operation-failure.json @@ -0,0 +1,56 @@ +{ + "description": "operation-failure", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-failure" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "Unsupported command", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "unsupportedCommand", + "command": { + "unsupportedCommand": 1 + } + } + } + ] + }, + { + "description": "Unsupported query operator", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "$unsupportedQueryOperator": 1 + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-fail/operation-unsupported.json b/tests/UnifiedSpecTests/valid-fail/operation-unsupported.json new file mode 100644 index 000000000..d8ef5ab1c --- /dev/null +++ b/tests/UnifiedSpecTests/valid-fail/operation-unsupported.json @@ -0,0 +1,22 @@ +{ + "description": "operation-unsupported", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "Unsupported operation", + "operations": [ + { + "name": "unsupportedOperation", + "object": "client0" + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/assertNumberConnectionsCheckedOut.json b/tests/UnifiedSpecTests/valid-pass/assertNumberConnectionsCheckedOut.json new file mode 100644 index 000000000..a9fc063f3 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/assertNumberConnectionsCheckedOut.json @@ -0,0 +1,27 @@ +{ + "description": "assertNumberConnectionsCheckedOut", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true + } + } + ], + "tests": [ + { + "description": "basic assertion succeeds", + "operations": [ + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 0 + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/collectionData-createOptions.json b/tests/UnifiedSpecTests/valid-pass/collectionData-createOptions.json new file mode 100644 index 000000000..19edc2247 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/collectionData-createOptions.json @@ -0,0 +1,79 @@ +{ + "description": "collectionData-createOptions", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0", + "createOptions": { + "capped": true, + "size": 4096 + }, + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "collection is created with the correct options", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$collStats": { + "storageStats": {} + } + }, + { + "$project": { + "capped": "$storageStats.capped", + "maxSize": "$storageStats.maxSize" + } + } + ] + }, + "expectResult": [ + { + "capped": true, + "maxSize": 4096 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/createEntities-operation.json b/tests/UnifiedSpecTests/valid-pass/createEntities-operation.json new file mode 100644 index 000000000..3fde42919 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/createEntities-operation.json @@ -0,0 +1,74 @@ +{ + "description": "createEntities-operation", + "schemaVersion": "1.9", + "tests": [ + { + "description": "createEntities operation", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "database1" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll1" + } + } + ] + } + }, + { + "name": "deleteOne", + "object": "collection1", + "arguments": { + "filter": { + "_id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll1", + "deletes": [ + { + "q": { + "_id": 1 + }, + "limit": 1 + } + ] + }, + "commandName": "delete", + "databaseName": "database1" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/entity-client-cmap-events.json b/tests/UnifiedSpecTests/valid-pass/entity-client-cmap-events.json new file mode 100644 index 000000000..3209033de --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/entity-client-cmap-events.json @@ -0,0 +1,71 @@ +{ + "description": "entity-client-cmap-events", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "connectionReadyEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [] + } + ], + "tests": [ + { + "description": "events are captured during an operation", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/entity-client-storeEventsAsEntities.json b/tests/UnifiedSpecTests/valid-pass/entity-client-storeEventsAsEntities.json new file mode 100644 index 000000000..e37e5a1ac --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/entity-client-storeEventsAsEntities.json @@ -0,0 +1,67 @@ +{ + "description": "entity-client-storeEventsAsEntities", + "schemaVersion": "1.2", + "createEntities": [ + { + "client": { + "id": "client0", + "storeEventsAsEntities": [ + { + "id": "client0_events", + "events": [ + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "test", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "storeEventsAsEntities captures events", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/entity-cursor-iterateOnce.json b/tests/UnifiedSpecTests/valid-pass/entity-cursor-iterateOnce.json new file mode 100644 index 000000000..b17ae78b9 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/entity-cursor-iterateOnce.json @@ -0,0 +1,111 @@ +{ + "description": "entity-cursor-iterateOnce", + "schemaVersion": "1.9", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "databaseName": "database0", + "collectionName": "coll0", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ], + "tests": [ + { + "description": "iterateOnce", + "operations": [ + { + "name": "createFindCursor", + "object": "collection0", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 1 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 2 + } + }, + { + "name": "iterateOnce", + "object": "cursor0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {}, + "batchSize": 2 + }, + "commandName": "find", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0" + }, + "commandName": "getMore" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/entity-find-cursor.json b/tests/UnifiedSpecTests/valid-pass/entity-find-cursor.json new file mode 100644 index 000000000..6f955d81f --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/entity-find-cursor.json @@ -0,0 +1,191 @@ +{ + "description": "entity-find-cursor", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "databaseName": "database0Name", + "collectionName": "coll0", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + }, + { + "_id": 5 + } + ] + } + ], + "tests": [ + { + "description": "cursors can be created, iterated, and closed", + "operations": [ + { + "name": "createFindCursor", + "object": "collection0", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 1 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 2 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectResult": { + "_id": 3 + } + }, + { + "name": "close", + "object": "cursor0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {}, + "batchSize": 2 + }, + "commandName": "find", + "databaseName": "database0Name" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": { + "$$type": [ + "int", + "long" + ] + }, + "ns": { + "$$type": "string" + }, + "firstBatch": { + "$$type": "array" + } + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0" + }, + "commandName": "getMore" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursor": { + "id": { + "$$type": [ + "int", + "long" + ] + }, + "ns": { + "$$type": "string" + }, + "nextBatch": { + "$$type": "array" + } + } + }, + "commandName": "getMore" + } + }, + { + "commandStartedEvent": { + "command": { + "killCursors": "coll0", + "cursors": { + "$$type": "array" + } + }, + "commandName": "killCursors" + } + }, + { + "commandSucceededEvent": { + "reply": { + "cursorsKilled": { + "$$unsetOrMatches": { + "$$type": "array" + } + } + }, + "commandName": "killCursors" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/expectedError-errorResponse.json b/tests/UnifiedSpecTests/valid-pass/expectedError-errorResponse.json new file mode 100644 index 000000000..177b1baf5 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/expectedError-errorResponse.json @@ -0,0 +1,70 @@ +{ + "description": "expectedError-errorResponse", + "schemaVersion": "1.12", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "Unsupported command", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "unsupportedCommand", + "command": { + "unsupportedCommand": 1 + } + }, + "expectError": { + "errorResponse": { + "errmsg": { + "$$type": "string" + } + } + } + } + ] + }, + { + "description": "Unsupported query operator", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "$unsupportedQueryOperator": 1 + } + }, + "expectError": { + "errorResponse": { + "errmsg": { + "$$type": "string" + } + } + } + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-eventType.json b/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-eventType.json new file mode 100644 index 000000000..fe308df96 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-eventType.json @@ -0,0 +1,126 @@ +{ + "description": "expectedEventsForClient-eventType", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent", + "connectionReadyEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [] + } + ], + "tests": [ + { + "description": "eventType can be set to command and cmap", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1 + } + ] + }, + "commandName": "insert" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + } + ] + } + ] + }, + { + "description": "eventType defaults to command if unset", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1 + } + ] + }, + "commandName": "insert" + } + } + ] + }, + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json b/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json new file mode 100644 index 000000000..178b756c2 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json @@ -0,0 +1,151 @@ +{ + "description": "expectedEventsForClient-ignoreExtraEvents", + "schemaVersion": "1.7", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [] + } + ], + "tests": [ + { + "description": "ignoreExtraEvents can be set to false", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": false, + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 1 + } + ] + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "ignoreExtraEvents can be set to true", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 2 + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 3 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2 + } + ] + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "ignoreExtraEvents defaults to false if unset", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 4 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 4 + } + ] + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/ignoreResultAndError.json b/tests/UnifiedSpecTests/valid-pass/ignoreResultAndError.json new file mode 100644 index 000000000..2e9b1c58a --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/ignoreResultAndError.json @@ -0,0 +1,59 @@ +{ + "description": "ignoreResultAndError", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [] + } + ], + "tests": [ + { + "description": "operation errors are ignored if ignoreResultAndError is true", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + }, + "ignoreResultAndError": true + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/kmsProviders-explicit_kms_credentials.json b/tests/UnifiedSpecTests/valid-pass/kmsProviders-explicit_kms_credentials.json new file mode 100644 index 000000000..7cc74939e --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/kmsProviders-explicit_kms_credentials.json @@ -0,0 +1,52 @@ +{ + "description": "kmsProviders-explicit_kms_credentials", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": "accessKeyId", + "secretAccessKey": "secretAccessKey" + }, + "azure": { + "tenantId": "tenantId", + "clientId": "clientId", + "clientSecret": "clientSecret" + }, + "gcp": { + "email": "email", + "privateKey": "cHJpdmF0ZUtleQo=" + }, + "kmip": { + "endpoint": "endpoint" + }, + "local": { + "key": "a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5" + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/kmsProviders-mixed_kms_credential_fields.json b/tests/UnifiedSpecTests/valid-pass/kmsProviders-mixed_kms_credential_fields.json new file mode 100644 index 000000000..363f2a457 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/kmsProviders-mixed_kms_credential_fields.json @@ -0,0 +1,54 @@ +{ + "description": "kmsProviders-mixed_kms_credential_fields", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": "accessKeyId", + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": "tenantId", + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": "email", + "privateKey": { + "$$placeholder": 1 + } + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/kmsProviders-placeholder_kms_credentials.json b/tests/UnifiedSpecTests/valid-pass/kmsProviders-placeholder_kms_credentials.json new file mode 100644 index 000000000..3f7721f01 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/kmsProviders-placeholder_kms_credentials.json @@ -0,0 +1,70 @@ +{ + "description": "kmsProviders-placeholder_kms_credentials", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/kmsProviders-unconfigured_kms.json b/tests/UnifiedSpecTests/valid-pass/kmsProviders-unconfigured_kms.json new file mode 100644 index 000000000..12ca58094 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/kmsProviders-unconfigured_kms.json @@ -0,0 +1,39 @@ +{ + "description": "kmsProviders-unconfigured_kms", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": {}, + "azure": {}, + "gcp": {}, + "kmip": {}, + "local": {} + } + } + } + } + ], + "tests": [ + { + "description": "", + "skipReason": "DRIVERS-2280: waiting on driver support for on-demand credentials", + "operations": [] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/matches-lte-operator.json b/tests/UnifiedSpecTests/valid-pass/matches-lte-operator.json new file mode 100644 index 000000000..4de65c583 --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/matches-lte-operator.json @@ -0,0 +1,78 @@ +{ + "description": "matches-lte-operator", + "schemaVersion": "1.9", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [] + } + ], + "tests": [ + { + "description": "special lte matching operator", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "y": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": { + "$$lte": 1 + }, + "y": { + "$$lte": 2 + } + } + ] + }, + "commandName": "insert", + "databaseName": "database0Name" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/observeSensitiveCommands.json b/tests/UnifiedSpecTests/valid-pass/observeSensitiveCommands.json new file mode 100644 index 000000000..d3ae5665b --- /dev/null +++ b/tests/UnifiedSpecTests/valid-pass/observeSensitiveCommands.json @@ -0,0 +1,706 @@ +{ + "description": "observeSensitiveCommands", + "schemaVersion": "1.5", + "runOnRequirements": [ + { + "auth": false + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ], + "observeSensitiveCommands": true + } + }, + { + "client": { + "id": "client1", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ], + "observeSensitiveCommands": false + } + }, + { + "client": { + "id": "client2", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "observeSensitiveCommands" + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "observeSensitiveCommands" + } + }, + { + "database": { + "id": "database2", + "client": "client2", + "databaseName": "observeSensitiveCommands" + } + } + ], + "tests": [ + { + "description": "getnonce is observed with observeSensitiveCommands=true", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "getnonce", + "command": { + "getnonce": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "getnonce", + "command": { + "getnonce": { + "$$exists": false + } + } + } + }, + { + "commandSucceededEvent": { + "commandName": "getnonce", + "reply": { + "ok": { + "$$exists": false + }, + "nonce": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "getnonce is not observed with observeSensitiveCommands=false", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database1", + "arguments": { + "commandName": "getnonce", + "command": { + "getnonce": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [] + } + ] + }, + { + "description": "getnonce is not observed by default", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database2", + "arguments": { + "commandName": "getnonce", + "command": { + "getnonce": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client2", + "events": [] + } + ] + }, + { + "description": "hello with speculativeAuthenticate", + "runOnRequirements": [ + { + "minServerVersion": "4.9" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + }, + { + "name": "runCommand", + "object": "database1", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + }, + { + "name": "runCommand", + "object": "database2", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "hello", + "command": { + "hello": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + }, + { + "commandSucceededEvent": { + "commandName": "hello", + "reply": { + "isWritablePrimary": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + } + ] + }, + { + "client": "client1", + "events": [] + }, + { + "client": "client2", + "events": [] + } + ] + }, + { + "description": "hello without speculativeAuthenticate is always observed", + "runOnRequirements": [ + { + "minServerVersion": "4.9" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1 + } + } + }, + { + "name": "runCommand", + "object": "database1", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1 + } + } + }, + { + "name": "runCommand", + "object": "database2", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "hello", + "command": { + "hello": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "hello", + "reply": { + "isWritablePrimary": { + "$$exists": true + } + } + } + } + ] + }, + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "commandName": "hello", + "command": { + "hello": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "hello", + "reply": { + "isWritablePrimary": { + "$$exists": true + } + } + } + } + ] + }, + { + "client": "client2", + "events": [ + { + "commandStartedEvent": { + "commandName": "hello", + "command": { + "hello": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "hello", + "reply": { + "isWritablePrimary": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "legacy hello with speculativeAuthenticate", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + }, + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + }, + { + "name": "runCommand", + "object": "database1", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + }, + { + "name": "runCommand", + "object": "database1", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + }, + { + "name": "runCommand", + "object": "database2", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + }, + { + "name": "runCommand", + "object": "database2", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "ismaster", + "command": { + "ismaster": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + }, + { + "commandSucceededEvent": { + "commandName": "ismaster", + "reply": { + "ismaster": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "isMaster", + "command": { + "isMaster": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + }, + { + "commandSucceededEvent": { + "commandName": "isMaster", + "reply": { + "ismaster": { + "$$exists": false + }, + "speculativeAuthenticate": { + "$$exists": false + } + } + } + } + ] + }, + { + "client": "client1", + "events": [] + }, + { + "client": "client2", + "events": [] + } + ] + }, + { + "description": "legacy hello without speculativeAuthenticate is always observed", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": 1 + } + } + }, + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": 1 + } + } + }, + { + "name": "runCommand", + "object": "database1", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": 1 + } + } + }, + { + "name": "runCommand", + "object": "database1", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": 1 + } + } + }, + { + "name": "runCommand", + "object": "database2", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": 1 + } + } + }, + { + "name": "runCommand", + "object": "database2", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "ismaster", + "command": { + "ismaster": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "ismaster", + "reply": { + "ismaster": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "isMaster", + "command": { + "isMaster": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "isMaster", + "reply": { + "ismaster": { + "$$exists": true + } + } + } + } + ] + }, + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "commandName": "ismaster", + "command": { + "ismaster": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "ismaster", + "reply": { + "ismaster": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "isMaster", + "command": { + "isMaster": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "isMaster", + "reply": { + "ismaster": { + "$$exists": true + } + } + } + } + ] + }, + { + "client": "client2", + "events": [ + { + "commandStartedEvent": { + "commandName": "ismaster", + "command": { + "ismaster": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "ismaster", + "reply": { + "ismaster": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "isMaster", + "command": { + "isMaster": 1 + } + } + }, + { + "commandSucceededEvent": { + "commandName": "isMaster", + "reply": { + "ismaster": { + "$$exists": true + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/valid-pass/poc-change-streams.json b/tests/UnifiedSpecTests/valid-pass/poc-change-streams.json index dc6e332e3..50f0d06f0 100644 --- a/tests/UnifiedSpecTests/valid-pass/poc-change-streams.json +++ b/tests/UnifiedSpecTests/valid-pass/poc-change-streams.json @@ -1,6 +1,11 @@ { "description": "poc-change-streams", - "schemaVersion": "1.0", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], "createEntities": [ { "client": { @@ -89,6 +94,42 @@ } ], "tests": [ + { + "description": "saveResultAsEntity is optional for createChangeStream", + "runOnRequirements": [ + { + "minServerVersion": "3.8.0", + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "client0", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1 + }, + "commandName": "aggregate", + "databaseName": "admin" + } + } + ] + } + ] + }, { "description": "Executing a watch helper on a MongoClient results in notifications for changes to all collections in all databases in the cluster.", "runOnRequirements": [ @@ -103,6 +144,9 @@ { "name": "createChangeStream", "object": "client0", + "arguments": { + "pipeline": [] + }, "saveResultAsEntity": "changeStream0" }, { @@ -246,7 +290,8 @@ "name": "createChangeStream", "object": "collection0", "arguments": { - "batchSize": 1 + "batchSize": 1, + "pipeline": [] }, "saveResultAsEntity": "changeStream0" }, diff --git a/tests/UnifiedSpecTests/valid-pass/poc-command-monitoring.json b/tests/UnifiedSpecTests/valid-pass/poc-command-monitoring.json index 499396e0b..fe0a5ae99 100644 --- a/tests/UnifiedSpecTests/valid-pass/poc-command-monitoring.json +++ b/tests/UnifiedSpecTests/valid-pass/poc-command-monitoring.json @@ -57,10 +57,11 @@ ], "tests": [ { - "description": "A successful find event with a getmore and the server kills the cursor", + "description": "A successful find event with a getmore and the server kills the cursor (<= 4.4)", "runOnRequirements": [ { "minServerVersion": "3.1", + "maxServerVersion": "4.4.99", "topologies": [ "single", "replicaset" diff --git a/tests/UnifiedSpecTests/valid-pass/poc-crud.json b/tests/UnifiedSpecTests/valid-pass/poc-crud.json index 2ed86d615..94e4ec568 100644 --- a/tests/UnifiedSpecTests/valid-pass/poc-crud.json +++ b/tests/UnifiedSpecTests/valid-pass/poc-crud.json @@ -1,6 +1,6 @@ { "description": "poc-crud", - "schemaVersion": "1.0", + "schemaVersion": "1.4", "createEntities": [ { "client": { @@ -242,12 +242,14 @@ }, "expectError": { "expectResult": { - "deletedCount": 0, - "insertedCount": 2, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} + "$$unsetOrMatches": { + "deletedCount": 0, + "insertedCount": 2, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } } } } @@ -320,8 +322,9 @@ "minServerVersion": "4.1.0", "topologies": [ "replicaset", - "sharded-replicaset" - ] + "sharded" + ], + "serverless": "forbid" } ], "operations": [ @@ -406,7 +409,8 @@ "description": "Aggregate with $listLocalSessions", "runOnRequirements": [ { - "minServerVersion": "3.6.0" + "minServerVersion": "3.6.0", + "serverless": "forbid" } ], "operations": [ diff --git a/tests/UnifiedSpecTests/valid-pass/poc-gridfs.json b/tests/UnifiedSpecTests/valid-pass/poc-gridfs.json index c04ed89a7..1f07a19bf 100644 --- a/tests/UnifiedSpecTests/valid-pass/poc-gridfs.json +++ b/tests/UnifiedSpecTests/valid-pass/poc-gridfs.json @@ -240,7 +240,9 @@ "uploadDate": { "$$type": "date" }, - "md5": "283d4fea5dded59cf837d3047328f5af", + "md5": { + "$$unsetOrMatches": "283d4fea5dded59cf837d3047328f5af" + }, "filename": "filename" } ] diff --git a/tests/UnifiedSpecTests/valid-pass/poc-retryable-writes.json b/tests/UnifiedSpecTests/valid-pass/poc-retryable-writes.json index e64ce1bce..da72b7f27 100644 --- a/tests/UnifiedSpecTests/valid-pass/poc-retryable-writes.json +++ b/tests/UnifiedSpecTests/valid-pass/poc-retryable-writes.json @@ -253,7 +253,7 @@ { "minServerVersion": "4.1.7", "topologies": [ - "sharded-replicaset" + "sharded" ] } ], @@ -297,11 +297,12 @@ "ordered": true }, "expectResult": { - "insertedCount": 2, - "insertedIds": { - "$$unsetOrMatches": { - "0": 3, - "1": 4 + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 3, + "1": 4 + } } } } @@ -344,7 +345,7 @@ { "minServerVersion": "4.1.7", "topologies": [ - "sharded-replicaset" + "sharded" ] } ], @@ -413,7 +414,7 @@ { "minServerVersion": "4.1.7", "topologies": [ - "sharded-replicaset" + "sharded" ] } ], diff --git a/tests/UnifiedSpecTests/valid-pass/poc-sessions.json b/tests/UnifiedSpecTests/valid-pass/poc-sessions.json index 75f348942..117c9e7d0 100644 --- a/tests/UnifiedSpecTests/valid-pass/poc-sessions.json +++ b/tests/UnifiedSpecTests/valid-pass/poc-sessions.json @@ -264,7 +264,7 @@ { "minServerVersion": "4.1.8", "topologies": [ - "sharded-replicaset" + "sharded" ] } ], diff --git a/tests/UnifiedSpecTests/valid-pass/poc-transactions-convenient-api.json b/tests/UnifiedSpecTests/valid-pass/poc-transactions-convenient-api.json index 820ed6592..9ab44a9c5 100644 --- a/tests/UnifiedSpecTests/valid-pass/poc-transactions-convenient-api.json +++ b/tests/UnifiedSpecTests/valid-pass/poc-transactions-convenient-api.json @@ -11,7 +11,7 @@ { "minServerVersion": "4.1.8", "topologies": [ - "sharded-replicaset" + "sharded" ] } ], diff --git a/tests/UnifiedSpecTests/valid-pass/poc-transactions-mongos-pin-auto.json b/tests/UnifiedSpecTests/valid-pass/poc-transactions-mongos-pin-auto.json index a0b297d59..de08edec4 100644 --- a/tests/UnifiedSpecTests/valid-pass/poc-transactions-mongos-pin-auto.json +++ b/tests/UnifiedSpecTests/valid-pass/poc-transactions-mongos-pin-auto.json @@ -5,7 +5,7 @@ { "minServerVersion": "4.1.8", "topologies": [ - "sharded-replicaset" + "sharded" ] } ], diff --git a/tests/UnifiedSpecTests/valid-pass/poc-transactions.json b/tests/UnifiedSpecTests/valid-pass/poc-transactions.json index 62528f9ce..2055a3b70 100644 --- a/tests/UnifiedSpecTests/valid-pass/poc-transactions.json +++ b/tests/UnifiedSpecTests/valid-pass/poc-transactions.json @@ -11,7 +11,7 @@ { "minServerVersion": "4.1.8", "topologies": [ - "sharded-replicaset" + "sharded" ] } ], @@ -61,14 +61,15 @@ "object": "session0" }, { - "name": "insertOne", + "name": "updateOne", "object": "collection0", "arguments": { "session": "session0", - "document": { - "_id": { - ".": "." - } + "filter": { + "_id": 1 + }, + "update": { + "x": 1 } }, "expectError": { @@ -92,7 +93,7 @@ "minServerVersion": "4.3.4", "topologies": [ "replicaset", - "sharded-replicaset" + "sharded" ] } ], @@ -202,7 +203,7 @@ "minServerVersion": "4.3.4", "topologies": [ "replicaset", - "sharded-replicaset" + "sharded" ] } ], diff --git a/tests/UnifiedSpecTests/versioned-api/crud-api-version-1-strict.json b/tests/UnifiedSpecTests/versioned-api/crud-api-version-1-strict.json new file mode 100644 index 000000000..c1c8ecce0 --- /dev/null +++ b/tests/UnifiedSpecTests/versioned-api/crud-api-version-1-strict.json @@ -0,0 +1,1109 @@ +{ + "description": "CRUD Api Version 1 (strict)", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.9" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ], + "serverApi": { + "version": "1", + "strict": true + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "versioned-api-tests" + } + }, + { + "database": { + "id": "adminDatabase", + "client": "client", + "databaseName": "admin" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "_yamlAnchors": { + "versions": [ + { + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + ] + }, + "initialData": [ + { + "collectionName": "test", + "databaseName": "versioned-api-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "tests": [ + { + "description": "aggregate on collection appends declared API version", + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "aggregate on database appends declared API version", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "adminDatabase", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + }, + "expectError": { + "errorCodeName": "APIStrictError" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "bulkWrite appends declared API version", + "operations": [ + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 6, + "x": 66 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteMany": { + "filter": { + "x": { + "$nin": [ + 24, + 34 + ] + } + } + } + }, + { + "updateMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 7 + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 4 + }, + "replacement": { + "_id": 4, + "x": 44 + }, + "upsert": true + } + } + ], + "ordered": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 6, + "x": 66 + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 2 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "x": { + "$nin": [ + 24, + 34 + ] + } + }, + "limit": 0 + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": 7 + }, + "limit": 1 + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 4 + }, + "u": { + "_id": 4, + "x": 44 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": true + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "countDocuments appends declared API version", + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": { + "x": { + "$gt": 11 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$match": { + "x": { + "$gt": 11 + } + } + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "deleteMany appends declared API version", + "operations": [ + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": { + "x": { + "$nin": [ + 24, + 34 + ] + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "x": { + "$nin": [ + 24, + 34 + ] + } + }, + "limit": 0 + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "deleteOne appends declared API version", + "operations": [ + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 7 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": 7 + }, + "limit": 1 + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "distinct appends declared API version", + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + }, + "expectError": { + "isError": true, + "errorContains": "command distinct is not in API Version 1", + "errorCodeName": "APIStrictError" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "test", + "key": "x", + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "estimatedDocumentCount appends declared API version", + "runOnRequirements": [ + { + "minServerVersion": "5.0.9", + "maxServerVersion": "5.0.99" + }, + { + "minServerVersion": "5.3.2" + } + ], + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "arguments": {} + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "test", + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "find and getMore append API version", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "batchSize": 3 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "findOneAndDelete appends declared API version", + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "remove": true, + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "findOneAndReplace appends declared API version", + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 33 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "update": { + "x": 33 + }, + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "findOneAndUpdate appends declared API version", + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "insertMany appends declared API version", + "operations": [ + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 6, + "x": 66 + }, + { + "_id": 7, + "x": 77 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 6, + "x": 66 + }, + { + "_id": 7, + "x": 77 + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "insertOne appends declared API version", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 6, + "x": 66 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 6, + "x": 66 + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "replaceOne appends declared API version", + "operations": [ + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 4 + }, + "replacement": { + "_id": 4, + "x": 44 + }, + "upsert": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 4 + }, + "u": { + "_id": 4, + "x": 44 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": true + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "updateMany appends declared API version", + "operations": [ + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "updateOne appends declared API version", + "operations": [ + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 2 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/versioned-api/crud-api-version-1.json b/tests/UnifiedSpecTests/versioned-api/crud-api-version-1.json new file mode 100644 index 000000000..a387d0587 --- /dev/null +++ b/tests/UnifiedSpecTests/versioned-api/crud-api-version-1.json @@ -0,0 +1,1101 @@ +{ + "description": "CRUD Api Version 1", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.9" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ], + "serverApi": { + "version": "1", + "deprecationErrors": true + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "versioned-api-tests" + } + }, + { + "database": { + "id": "adminDatabase", + "client": "client", + "databaseName": "admin" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + } + ], + "_yamlAnchors": { + "versions": [ + { + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + ] + }, + "initialData": [ + { + "collectionName": "test", + "databaseName": "versioned-api-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "tests": [ + { + "description": "aggregate on collection appends declared API version", + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "aggregate on database appends declared API version", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "adminDatabase", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "bulkWrite appends declared API version", + "operations": [ + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 6, + "x": 66 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteMany": { + "filter": { + "x": { + "$nin": [ + 24, + 34 + ] + } + } + } + }, + { + "updateMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 7 + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 4 + }, + "replacement": { + "_id": 4, + "x": 44 + }, + "upsert": true + } + } + ], + "ordered": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 6, + "x": 66 + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 2 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "x": { + "$nin": [ + 24, + 34 + ] + } + }, + "limit": 0 + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": 7 + }, + "limit": 1 + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 4 + }, + "u": { + "_id": 4, + "x": 44 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": true + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "countDocuments appends declared API version", + "operations": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": { + "x": { + "$gt": 11 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$match": { + "x": { + "$gt": 11 + } + } + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "deleteMany appends declared API version", + "operations": [ + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": { + "x": { + "$nin": [ + 24, + 34 + ] + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "x": { + "$nin": [ + 24, + 34 + ] + } + }, + "limit": 0 + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "deleteOne appends declared API version", + "operations": [ + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 7 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": 7 + }, + "limit": 1 + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "distinct appends declared API version", + "operations": [ + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "test", + "key": "x", + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "estimatedDocumentCount appends declared API version", + "runOnRequirements": [ + { + "minServerVersion": "5.0.9", + "maxServerVersion": "5.0.99" + }, + { + "minServerVersion": "5.3.2" + } + ], + "operations": [ + { + "name": "estimatedDocumentCount", + "object": "collection", + "arguments": {} + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "test", + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "find and getMore append API version", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "batchSize": 3 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "findOneAndDelete appends declared API version", + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "remove": true, + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "findOneAndReplace appends declared API version", + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 33 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "update": { + "x": 33 + }, + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "findOneAndUpdate appends declared API version", + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "insertMany appends declared API version", + "operations": [ + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 6, + "x": 66 + }, + { + "_id": 7, + "x": 77 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 6, + "x": 66 + }, + { + "_id": 7, + "x": 77 + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "insertOne appends declared API version", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 6, + "x": 66 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 6, + "x": 66 + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "replaceOne appends declared API version", + "operations": [ + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 4 + }, + "replacement": { + "_id": 4, + "x": 44 + }, + "upsert": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 4 + }, + "u": { + "_id": 4, + "x": 44 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": true + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "updateMany appends declared API version", + "operations": [ + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + }, + { + "description": "updateOne appends declared API version", + "operations": [ + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 2 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/versioned-api/runcommand-helper-no-api-version-declared.json b/tests/UnifiedSpecTests/versioned-api/runcommand-helper-no-api-version-declared.json new file mode 100644 index 000000000..17e0126d1 --- /dev/null +++ b/tests/UnifiedSpecTests/versioned-api/runcommand-helper-no-api-version-declared.json @@ -0,0 +1,127 @@ +{ + "description": "RunCommand helper: No API version declared", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.9", + "serverParameters": { + "requireApiVersion": false + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "versioned-api-tests" + } + } + ], + "tests": [ + { + "description": "runCommand does not inspect or change the command document", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1, + "apiVersion": "server_will_never_support_this_api_version" + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1, + "apiVersion": "server_will_never_support_this_api_version", + "apiStrict": { + "$$exists": false + }, + "apiDeprecationErrors": { + "$$exists": false + } + }, + "commandName": "ping", + "databaseName": "versioned-api-tests" + } + } + ] + } + ] + }, + { + "description": "runCommand does not prevent sending invalid API version declarations", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1, + "apiStrict": true + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1, + "apiVersion": { + "$$exists": false + }, + "apiStrict": true, + "apiDeprecationErrors": { + "$$exists": false + } + }, + "commandName": "ping", + "databaseName": "versioned-api-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/versioned-api/test-commands-deprecation-errors.json b/tests/UnifiedSpecTests/versioned-api/test-commands-deprecation-errors.json new file mode 100644 index 000000000..0668df830 --- /dev/null +++ b/tests/UnifiedSpecTests/versioned-api/test-commands-deprecation-errors.json @@ -0,0 +1,74 @@ +{ + "description": "Test commands: deprecation errors", + "schemaVersion": "1.1", + "runOnRequirements": [ + { + "minServerVersion": "4.9", + "serverParameters": { + "enableTestCommands": true, + "acceptApiVersion2": true, + "requireApiVersion": false + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "versioned-api-tests" + } + } + ], + "tests": [ + { + "description": "Running a command that is deprecated raises a deprecation error", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "testDeprecationInVersion2", + "command": { + "testDeprecationInVersion2": 1, + "apiVersion": "2", + "apiDeprecationErrors": true + } + }, + "expectError": { + "isError": true, + "errorContains": "command testDeprecationInVersion2 is deprecated in API Version 2", + "errorCodeName": "APIDeprecationError" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "testDeprecationInVersion2": 1, + "apiVersion": "2", + "apiStrict": { + "$$exists": false + }, + "apiDeprecationErrors": true + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/versioned-api/test-commands-strict-mode.json b/tests/UnifiedSpecTests/versioned-api/test-commands-strict-mode.json new file mode 100644 index 000000000..9c4ebea78 --- /dev/null +++ b/tests/UnifiedSpecTests/versioned-api/test-commands-strict-mode.json @@ -0,0 +1,75 @@ +{ + "description": "Test commands: strict mode", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.9", + "serverParameters": { + "enableTestCommands": true + }, + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ], + "serverApi": { + "version": "1", + "strict": true + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "versioned-api-tests" + } + } + ], + "tests": [ + { + "description": "Running a command that is not part of the versioned API results in an error", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "testVersion2", + "command": { + "testVersion2": 1 + } + }, + "expectError": { + "isError": true, + "errorContains": "command testVersion2 is not in API Version 1", + "errorCodeName": "APIStrictError" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "testVersion2": 1, + "apiVersion": "1", + "apiStrict": true, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/versioned-api/transaction-handling.json b/tests/UnifiedSpecTests/versioned-api/transaction-handling.json new file mode 100644 index 000000000..32031296a --- /dev/null +++ b/tests/UnifiedSpecTests/versioned-api/transaction-handling.json @@ -0,0 +1,348 @@ +{ + "description": "Transaction handling", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.9", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ], + "serverApi": { + "version": "1" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "versioned-api-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ], + "_yamlAnchors": { + "versions": [ + { + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + ] + }, + "initialData": [ + { + "collectionName": "test", + "databaseName": "versioned-api-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "tests": [ + { + "description": "All commands in a transaction declare an API version", + "runOnRequirements": [ + { + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "operations": [ + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 6, + "x": 66 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 6 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 7, + "x": 77 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 7 + } + } + } + }, + { + "name": "commitTransaction", + "object": "session" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 6, + "x": 66 + } + ], + "lsid": { + "$$sessionLsid": "session" + }, + "startTransaction": true, + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 7, + "x": 77 + } + ], + "lsid": { + "$$sessionLsid": "session" + }, + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session" + }, + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + }, + { + "description": "abortTransaction includes an API version", + "runOnRequirements": [ + { + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "operations": [ + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 6, + "x": 66 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 6 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 7, + "x": 77 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 7 + } + } + } + }, + { + "name": "abortTransaction", + "object": "session" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 6, + "x": 66 + } + ], + "lsid": { + "$$sessionLsid": "session" + }, + "startTransaction": true, + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 7, + "x": 77 + } + ], + "lsid": { + "$$sessionLsid": "session" + }, + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session" + }, + "apiVersion": "1", + "apiStrict": { + "$$unsetOrMatches": false + }, + "apiDeprecationErrors": { + "$$unsetOrMatches": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 31649c704..d10cd460d 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,23 +1,9 @@ register(new Int64Comparator()); diff --git a/tools/connect.php b/tools/connect.php new file mode 100644 index 000000000..121f727c7 --- /dev/null +++ b/tools/connect.php @@ -0,0 +1,137 @@ + ['capture_peer_cert' => true]] : []); + $client = @stream_socket_client($uri, $errno, $errorMessage, 5, STREAM_CLIENT_CONNECT, $context); + + if ($client === false) { + printf("Could not connect to %s: %s\n", $host, $errorMessage); + + return; + } + + if ($ssl) { + $peerCertificate = stream_context_get_params($client)['options']['ssl']['peer_certificate'] ?? null; + + if (! isset($peerCertificate)) { + printf("Could not capture peer certificate for %s\n", $host); + + return; + } + + $certificateProperties = openssl_x509_parse($peerCertificate); + + // TODO: Check that the certificate common name (CN) matches the hostname + $now = new DateTime(); + $validFrom = DateTime::createFromFormat('U', $certificateProperties['validFrom_time_t']); + $validTo = DateTime::createFromFormat('U', $certificateProperties['validTo_time_t']); + $isValid = $now >= $validFrom && $now <= $validTo; + + printf("Peer certificate for %s is %s\n", $host, $isValid ? 'valid' : 'expired'); + + if (! $isValid) { + printf(" Valid from %s to %s\n", $validFrom->format('c'), $validTo->format('c')); + } + } + + $request = pack( + 'Va*xVVa*', + 1 << 2 /* slaveOk */, + 'admin.$cmd', /* namespace */ + 0, /* numberToSkip */ + 1, /* numberToReturn */ + hex2bin('130000001069734d6173746572000100000000') /* { "isMaster": 1 } */ + ); + $requestLength = 16 /* MsgHeader length */ + strlen($request); + $header = pack('V4', $requestLength, 0 /* requestID */, 0 /* responseTo */, 2004 /* OP_QUERY */); + + if ($requestLength !== streamWrite($client, $header . $request)) { + printf("Could not write request to %s\n", $host); + + return; + } + + $data = streamRead($client, 4); + + if ($data === false || strlen($data) !== 4) { + printf("Could not read response header from %s\n", $host); + + return; + } + + [, $responseLength] = unpack('V', $data); + + $data = streamRead($client, $responseLength - 4); + + if ($data === false || strlen($data) !== $responseLength - 4) { + printf("Could not read response from %s\n", $host); + + return; + } + + printf("Successfully received response from %s\n", $host); +} + +$uri = $argv[1] ?? 'mongodb://127.0.0.1'; +printf("Looking up MongoDB at %s\n", $uri); +$hosts = getHosts($uri); +$ssl = stripos(parse_url($uri, PHP_URL_QUERY) ?? '', 'ssl=true') !== false; + +printf("Found %d host(s) in the URI. Will attempt to connect to each.\n", count($hosts)); + +foreach ($hosts as $host) { + echo "\n"; + connect($host, $ssl); +} diff --git a/tools/detect-extension.php b/tools/detect-extension.php new file mode 100644 index 000000000..4b599fd4b --- /dev/null +++ b/tools/detect-extension.php @@ -0,0 +1,98 @@ + $line) { + if (strpos($line, 'extension') === false) { + continue; + } + + if (strpos($line, $extension) === false) { + continue; + } + + $lines[$i] = $line; + } + + if (empty($lines)) { + printf("No interesting lines in %s.\n", $filename); + + return 0; + } + + printf("Interesting lines in %s...\n", $filename); + foreach ($lines as $i => $line) { + printf(" %d: %s\n", $i + 1, trim($line)); + } + + return count($lines); +} + +$extension = $argv[1] ?? 'mongodb'; +$extensionDir = ini_get('extension_dir'); + +$version = phpversion($extension); + +if ($version !== false) { + printf("Extension \"%s\" is loaded. Version: %s\n", $extension, $version); + exit; +} + +printf("Extension \"%s\" is not loaded. Will attempt to scan INI files.\n", $extension); + +// Check main INI file +$ini = php_ini_loaded_file(); +$lines = 0; + +if ($ini === false) { + printf("No php.ini file is loaded. Will attempt to scan additional INI files.\n"); +} else { + $lines += grepIniFile($ini, $extension); +} + +// Check additional INI files in scan directory +// See: https://www.php.net/manual/en/configuration.file.php#configuration.file.scan +$files = php_ini_scanned_files(); + +if (empty($files)) { + printf("No additional INI files are loaded. Nothing left to scan.\n"); +} else { + foreach (explode(',', $files) as $ini) { + $lines += grepIniFile(trim($ini), $extension); + } +} + +$mask = defined('PHP_WINDOWS_VERSION_BUILD') ? 'php_%s.dll' : '%s.so'; +$filename = sprintf($mask, $extension); +$extensionFileExists = file_exists($extensionDir . '/' . $filename); + +echo "\n"; +printf("PHP will look for extensions in: %s\n", $extensionDir); +printf("Checking if that directory is readable: %s\n", is_dir($extensionDir) || ! is_readable($extensionDir) ? 'yes' : 'no'); +printf("Checking if extension file exists in that directory: %s\n", $extensionFileExists ? 'yes' : 'no'); +echo "\n"; + +if ($extensionFileExists) { + printf("A file named %s exists in the extension directory. Make sure you have enabled the extension in php.ini.\n", $filename); +} elseif (! defined('PHP_WINDOWS_VERSION_BUILD')) { + // Installation instructions for non-Windows systems are only necessary if the extension file does not exist. + printf("You should install the extension using the pecl command in %s\n", PHP_BINDIR); + printf("After installing the extension, you should add \"extension=%s\" to php.ini\n", $filename); +} + +if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $zts = PHP_ZTS ? 'Thread Safe (TS)' : 'Non Thread Safe (NTS)'; + $arch = PHP_INT_SIZE === 8 ? 'x64' : 'x86'; + $dll = sprintf("%d.%d %s %s", PHP_MAJOR_VERSION, PHP_MINOR_VERSION, $zts, $arch); + + printf("You likely need to download a Windows DLL for: %s\n", $dll); + printf("Windows DLLs should be available from: https://pecl.php.net/package/%s\n", $extension); + + if ($extensionFileExists) { + echo "If you have enabled the extension in php.ini and it is not loading, make sure you have downloaded the correct DLL file as indicated above.\n"; + } else { + printf("After installing the extension, you should add \"extension=%s\" to php.ini\n", $filename); + } +}